oracle, join with special requests - oracle

I have three tables with articles, inventory and warehouse.
CREATE TABLE "ARTICLE"
( "ID" NUMBER(9,0),
"NAME" VARCHAR2(30 BYTE)
)
CREATE TABLE "INVENTORY"
( "ID" NUMBER(9,0),
"ARTICLE_ID" NUMBER(9,0),
"QUANTITY" NUMBER(9,0),
"WAREHOUSE_ID" NUMBER(9,0)
)
CREATE TABLE "WAREHOUSE"
( "ID" NUMBER(9,0),
"NAME" VARCHAR2(30 BYTE)
)
I created a view, with shows me all inventory (with article name, etc.)
CREATE VIEW "INVENTORY_VIEW" AS
a.ID AS A_ID,
a.NAME AS A_NAME,
i.ID AS I_ID,
i.QUANTITY AS I_QUANTITY,
i.WAREHOUSE_ID AS I_WAREHOUSE_ID
FROM
ARTICLE a
LEFT OUTER JOIN INVENTORY i ON i.ARTICLE_ID = a.ID
I did it to make sure, I get a row for each article, even if there's no row in inventory. (I_WAREHOUSE_ID is null in that special case)
This works fine except for one case:
As soon as there is one row in INVENTORY for another warehouse of course the row (with I_WAREHOUSE_ID = null) disapears for the other warehouses.
(The other warehouses (e.g. Warehouse 2) select the inventory by "WHERE I_WAREHOUSE_ID=2" or "I_WAREHOUSE_ID is null".)
Anybody an idea how to solve this?
What I need is a article row for each WAREHOUSE_ID (with ID) if there is no iventory row for this article and warehouse.
My idea was to create a second view with all article / warehouse combinations and then kind of join that table with the inventory, but I had no success.

If I understand you correctly, you want one result row per article and warehouse. So cross join the two tables and outer join the inventory then:
CREATE VIEW inventory_view AS
SELECT
a.id AS article_id,
a.name AS article_name,
i.id AS inventory_id,
i.quantity AS inventory_quantity,
w.id AS warehouse_id
FROM ARTICLE a
CORSS JOIN WAREHOUSE w
LEFT OUTER JOIN inventory i ON i.article_id = a.id AND i.warehouse_id = w.id;

If I understand correctly what you asked (pls next time post expected result in tabular text) you could try following query:
SELECT
A.ID AS A_ID,
A.NAME AS A_NAME,
I.ID AS I_ID,
I.QUANTITY AS I_QUANTITY,
I.WAREHOUSE_ID AS I_WAREHOUSE_ID
, W2.NAME
, W2.ID
FROM ARTICLE A
LEFT JOIN INVENTORY I ON I.ARTICLE_ID = A.ID
CROSS JOIN WAREHOUSE W2
WHERE I.WAREHOUSE_ID IS NULL OR (W2.ID IS NOT NULL AND I.WAREHOUSE_ID=W2.ID);
Sample data:
INSERT INTO ARTICLE VALUES (1,'PROD_A');
INSERT INTO ARTICLE VALUES (2,'PROD_B');
INSERT INTO INVENTORY VALUES (1, 1,10,2);
INSERT INTO "WAREHOUSE" VALUES (1,'WA');
INSERT INTO "WAREHOUSE" VALUES (2,'WB');
Output:
+------+--------+------+------------+----------------+------+----+
| A_ID | A_NAME | I_ID | I_QUANTITY | I_WAREHOUSE_ID | NAME | ID |
+------+--------+------+------------+----------------+------+----+
| 1 | PROD_A | 1 | 10 | 2 | WB | 2 |
| 2 | PROD_B | NULL | NULL | NULL | WA | 1 |
| 2 | PROD_B | NULL | NULL | NULL | WB | 2 |
+------+--------+------+------------+----------------+------+----+

Related

Vertica Table Analysis

I would like to analyze table usage on Verica to check the following
the tables that are hit most be queries
tables that are getting more write queries
tables that are getting more read queries.
So I am asking for help for SQL query or if anyone has any documents please point me in right direction. Thank you.
Here, I create a function QTYPE() that assigns a request of type 'QUERY' to either a SELECT, an INSERT, or a MODIFY (meaning DELETE,UPDATE,MERGE). The differentiation comes from the fact that, in Vertica, UPDATE/MERGE are actually DELETEs, then INSERTs.
I use two regular expressions of a certain complexity: first, finding [schema.]tablename after a JOIN or FROM keyword, then finding [schema.]tablename after either the UPDATE, the INSERT INTO, the MERGE INTO and the DELETE FROM keywords. Then, I join back to the tables system table to a) only select the tables really existing and b) add the schema name if it is missing.
The final report would be:
qtype | tbname | tx_count
--------+------------------------------------------------------+----------
INSERT | dbadmin.nrm_cpustats_rate | 74
INSERT | dbadmin.v_poll_item | 39
INSERT | dbadmin.child | 32
INSERT | dbadmin.tbid | 32
INSERT | dbadmin.etl_group_membership | 12
INSERT | dbadmin.sensor_oco | 11
INSERT | webanalytics.webtraffic_part | 10
INSERT | webanalytics.webtraffic_new_design_platform_datadate | 9
MODIFY | cp.foo | 2
MODIFY | public.foo | 2
MODIFY | taboola_tests.foo | 2
SELECT | dbadmin.flext | 112
SELECT | dbadmin.children | 112
SELECT | dbadmin.ffoo | 112
SELECT | dbadmin.demovals | 112
SELECT | dbadmin.allbut4 | 112
SELECT | dbadmin.allcols | 112
SELECT | dbadmin.allbut1 | 112
SELECT | dbadmin.flx | 112
Here's the function definition, and the CREATE TABLE statement to collect the statistics of what you're looking for, and finally the query getting the 'hit parade' of the most touched tables ...
Mind you, it might become a long runner with a lot of history in your query_requests table ...
CREATE OR REPLACE FUNCTION qtype(sql VARCHAR(64000))
RETURN VARCHAR(8) AS BEGIN
RETURN
CASE UPPER(REGEXP_SUBSTR(sql,'\w+')::VARCHAR(16))
WHEN 'SELECT' THEN 'SELECT'
WHEN 'WITH' THEN 'SELECT'
WHEN 'AT' THEN 'SELECT'
WHEN 'INSERT' THEN 'INSERT'
WHEN 'DELETE' THEN 'MODIFY'
WHEN 'UPDATE' THEN 'MODIFY'
WHEN 'MERGE' THEN 'MODIFY'
ELSE UPPER(REGEXP_SUBSTR(sql,'\w+')::VARCHAR(16))
END
;
END;
DROP TABLE IF EXISTS table_op_stats;
CREATE TABLE table_op_stats AS
WITH
-- need 1000 integers - up to ~400 source tables found in 1 select
i(i) AS (
SELECT MICROSECOND(tm)
FROM (
SELECT TIMESTAMPADD(MICROSECOND, 1,'2000-01-01'::TIMESTAMP)
UNION ALL SELECT TIMESTAMPADD(MICROSECOND,1000,'2000-01-01'::TIMESTAMP)
) l(ts)
TIMESERIES tm AS '1 MICROSECOND' OVER(ORDER BY ts)
)
,
tblist AS (
-- selects can affect several types, found by JOIN or FROM keyword before
-- hence look_behind regular expression
SELECT
QTYPE(request) AS qtype
, transaction_id
, statement_id
, i
, LTRIM(REGEXP_SUBSTR(request,'(?<=(from|join))\s+(\w+\.)?\w+\b',1,i,'i')) as tbname
FROM query_requests CROSS JOIN i
WHERE request_type='QUERY'
AND success
AND LTRIM(REGEXP_SUBSTR(request,'(?<=(from|join))\s+(\w+\.)?\w+\b',1,i,'i')) <> ''
UNION ALL
-- insert/delete/update/merge queries only affect one table each
SELECT
QTYPE(request) AS qtype
, transaction_id
, statement_id
, 1 AS i
, LTRIM(REGEXP_SUBSTR(request,'(insert\s+.*into\s+|update\s+.*|merge\s+.*into|delete\s+.*from)\s*((\w+\.)?\w+)\b',1,1,'i',2)) as tbname
FROM query_requests
WHERE request_type='QUERY'
AND success
AND QTYPE(request) <> 'SELECT'
)
,
-- join back to the "tables" system table - removes queries from correlation names, and adds schema name if needed
real_tables AS (
SELECT
qtype
, transaction_id
, statement_id
, i
, CASE WHEN SPLIT_PART(tbname,'.',2)=''
THEN table_schema||'.'||tbname
ELSE tbname
END AS tbname
FROM tblist
JOIN tables ON CASE WHEN SPLIT_PART(tbname,'.',2)=''
THEN tbname=table_name
ELSE SPLIT_PART(tbname,'.',1)=table_schema AND SPLIT_PART(tbname,'.',2)=table_name
END
)
SELECT
qtype
, transaction_id
, statement_id
, i
, tbname
FROM real_tables;
-- Time: First fetch (0 rows): 42483.769 ms. All rows formatted: 42484.324 ms
-- the query at the end:
WITH grp AS (
SELECT
qtype
, tbname
, COUNT(*) AS tx_count
FROM table_op_stats
GROUP BY 1,2
)
SELECT
*
FROM grp
LIMIT 8 OVER(
PARTITION BY qtype
ORDER BY tx_count DESC
);

How to get mismatch records of two tables from same database in hive?

Eg:
select username, country from table1
Minus
Select username, country from table2;
The above minus query works in RDBMS but i want the same result using hive. Can we use joins here in hive to get the result? If so how to get proper result using hive query.
Set operations (MINUS/EXCEPT/INTERSECT in addition to UNION) are supported as of Hive 2.3.0 (released on 17 July 2017)
https://issues.apache.org/jira/browse/HIVE-12764
Demo
create table table1 (username string, country string);
create table table2 (username string, country string);
insert into table1 values ('Danny','USA'),('Danny','USA'),('David','UK');
insert into table2 values ('David','UK'),('Michal','France');
select username, country from table1
minus
Select username, country from table2
;
+--------------+-------------+
| _u1.username | _u1.country |
+--------------+-------------+
| Danny | USA |
+--------------+-------------+
In older Hive version you can use -
select username
,country
from ( select 1 tab,username, country from table1
union all select 2 tab,username, country from table2
) t
group by username
,country
having count(case when tab = 2 then 1 end) = 0
;
+----------+---------+
| username | country |
+----------+---------+
| Danny | USA |
+----------+---------+
You may utilize left join as follows
select table1.username, table1.country
from table1 left join table2
on table1.username=table2.username and table1.country=table2.country
where table2.username is NULL and table2.country is NULL;
Yes , As minus and exist not usually work in hive we can do minus operation by below LEFT JOIN condition.
SELECT t1.username, t1.country
FROM
(select username, country from table1) t1
LEFT JOIN
(Select username, country from table2) t2
ON t1.username =t2.username
AND t1.country =t2.country
WHERE t1.username IS NULL
IMP NOTE:Please do use WHERE CLAUSE FOR NULL Operations instead of AND after join condition this will have different results.

Retrieve from Oracle db key value pair

I need to retrieve 3 values with different key from a key value pair table.
My database schema as follows. I need to reach to table3 from table1 by taking the E_SUBID and then joining the table2 with E_SUBID. Once table1 and table2 are joined I need take to take E_CID from table2 to join it with table2 E_CID to get the "Attr_Value" keeping E_CID as a criteria.
Table1
------------------------
|E_SUBID| B_LocationID |
|1 100 |
|2 101 |
|3 102 |
Table2
-----------------
|E_CID | E_SUBID|
|10 1 |
|11 2 |
|12 3 |
Table3
---------------------------------
|E_CID | Attr_name | Attr_Value |
|10 Product Samsung |
|10 Model Smartphone |
|10 usage daily |
|11 Product Apple |
|11 Model Ipad |
|11 usage everyday |
|12 Model smartwatch |
I have been successful to join table1,table2 and table3 but I cannot get the required output which as follows
OUTPUT
|Product | Model | Usage |
Samsung Smartphone daily
Apple Ipad everyday
null smartwatch null
The query which joins table1, table2 and table3 as follows
select distinct t3.Attr_value as Product
from table1 t1, table2 t2, table3 t3
where t1.E_SUBID = t2.E_SUBID and
t2.E_CID = t3.E_CID and
t3.Attr_name=?????
order by Product;
Thank you for your time.
In a case like this, you can join to table3 as often as you need to for each attribute name you wish to display:
select
p.attr_value product,
m.attr_value "model", -- Quotes to escape reserved word
u.attr_value usage
from table1 t1
join table2 t2 on t1.e_subid = t2.e_subid
left outer join table3 p on t2.e_cid = p.e_cid and p.attr_name = 'Product'
left outer join table3 m on t2.e_cid = m.e_cid and m.attr_name = 'Model'
left outer join table3 u on t2.e_cid = u.e_cid and u.attr_name = 'Usage'
order by 1;
Edit
Based on the comment, by making table3 optional (outer join) the query should return all rows and whether or not a Model or Usage or Product has been defined.
Try as below ... Basically you are trying to transpose the rows to column in table3.
Select Product, "Model", Usage
From
(
Select
t1.E_SUBID,
t2.E_CID,
Max(Case when T3.Attr_name = 'Product' Then T3.Attr_Value else null end) Product,
max(Case when T3.Attr_name = 'Model' Then T3.Attr_Value else null end) Model,
max(Case when T3.Attr_name = 'Usage' Then T3.Attr_Value else null end) Usage
From Table1 t1,
Table2 t2,
Table3 t3
Where
t1.E_SUBID = t2.E_SUBID
and t2.E_CID = t3.E_CID
group by t1.t1.E_SUBID,t2.E_CID
);

Complex SQL query to join two tables

Problem:
Given two tables: TableA, TableB, where TableA has a one-to-many relationship with TableB, I want to retrieve all records in TableB for where the search criteria matches a certain column in TableB and return NULL for the unique TableA records for the same attribute.
Table Structures:
Table A
ID(Primary Key) | Name | City
1 | ABX | San Francisco
2 | ASDF | Oakland
3 | FDFD | New York
4 | GFGF | Austin
5 | GFFFF | San Francisco
Table B
ATTR_ID |Attr_Type | Attr_Name | Attr_Value
1 | TableA | Attr_1 | Attr_Value_1
2 | TableD | Attr_1 | Attr_Value_2
1 | TableA | Attr_2 | Attr_Value_3
3 | TableA | Attr_4 | Attr_Value_4
9 | TableC | Attr_2 | Attr_Value_5
Table B holds attribtue names and values and is a common table used across multiple tables. Each table is identified by Attr_Type and ATTR_ID (which maps to the IDs of different tables).
For instance, the record in Table A with ID 1 has two attributes in Table B with Attr_Names: Attr_1 and Attr_2 and so on.
Expected Output
ID | Name | City | TableB.Attr_Value
1 | ABX | San Francisco | Attr_Value_1
2 | ASDF | Oakland | Attr_Value_2
3 | FDFD | New York | NULL
4 | GFGF | Austin | NULL
5 | GFFFF | San Francisco | NULL
Search Criteria:
Get rows from Table B for each record in Table A with ATTR_NAME Attr_1. If a particular TableA record doesn't have Attr_1, return null.
My Query
select id, name, city,
b.attr_value from table_A
join table_B b on
table_A.id =b.attr_id and b.attr_name='Attr_1'
This is a strange data structure. You need a left outer join with the conditions in the on clause:
select a.id, a.name, a.city, b.attr_value
from table_A a left join
table_B b
on a.id = b.attr_id and b.attr_name = 'Attr_1' and b.attr_type = 'TableA';
I added the attr_type condition, because that seems logic with this data structure.
I dont have an sql server to test the command, but what you want is an inner/outer join query. You could do something like this
select id, name, city,
b.attr_value from table_A
join table_B b on
table_A.id *= b.attr_id and b.attr_name *= 'Attr_1'
Something like this should do the trick for you

Using DISTINCT for specific columns

select distinct employee_id, first_name, commission_pct, department_id from
employees;
When I use the above query it results in distinct combination of all the attributes mentioned. As employee_id (being the primary key for employees) is unique, the query results in producing all the rows in the table.
I want to have a result set that has distinct combination of commission_pct and department_id. so how the query should be formed. When I tried to include the DISTINCT in the middle as
select employee_id, first_name, distinct commission_pct, department_id from
employees;
It is resulting in an error
ORA-00936-missing expression
How to form a query which results have only distinct combination of commission and department_id.The table is from HR schema of oracle.
What you request is impossible. You cannot select all the employee ids but have only distinct commission_pct and department_id.
So think it over, what you want to show:
All distinct commission_pct, department_id only?
All distinct commission_pct, department_id and the number of relevant employees?
All distinct commission_pct, department_id and the relevant employees comma separated?
All employees, but with nulls when commission_pct and department_id are the same as in the line before?
The first can be solved with DISTINCT. The second and third with GROUP BY (plus count or listagg). The last would be solved with the analytic function LAG.
You have to remove two columns before distinct
select distinct commission_pct, department_id from
employees;
Indeed, if your second query would work, what do you expect to see in the first two columns? Consider example data
| employee_id | first_name | commission_pct | department_id |
| 1 | "x" | "b" | 3 |
| 2 | "y" | "b" | 3 |
| 1 | "x" | "c" | 4 |
| 2 | "y" | "c" | 4 |
You expect to get only two row result like this
| employee_id | first_name | commission_pct | department_id |
| ? | ? | "b" | 3 |
| ? | ? | "c" | 4 |
But what do you expect in the first two column?
Can you try this one?
SELECT
NAME1,
PH
FROM
(WITH T
AS (SELECT
'mark' NAME1,
'1234567' PH
FROM
DUAL
UNION ALL
SELECT
'bailey',
'456789'
FROM
DUAL
UNION ALL
SELECT
'mark',
'987654'
FROM
DUAL)
SELECT
NAME1,
PH,
ROW_NUMBER ( ) OVER (PARTITION BY NAME1 ORDER BY NAME1) SEQ
FROM
T)
WHERE
SEQ = 1;
If you dont care on a specific row, then use aggregate functions
SELECT
NAME1,
MAX ( PH ) PH
FROM
T
GROUP BY
NAME1;

Resources