Convert Rows to Columns in one SQL but not impact number rows - sql

I have a table its structure and data look like this:
then I want a SQL to convert it like this:
I really don't know how to write a SQL to accomplish this function, can anyone help me? I have referenced a lot of previous answers for this kind of topic but I cannot find one for my case. can anyone help me, please.

You can do it with a hierarchical query if you only have three levels to consider:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE table_name ( LineItem_Name, LineItem_Id, parent_id, dept_name, product_name ) AS
SELECT 'ABC', 1, NULL, 'D1', 'P1' FROM DUAL UNION ALL
SELECT 'CDF', 2, 1, 'D2', 'P2' FROM DUAL UNION ALL
SELECT 'EFG', 3, 1, 'D3', 'P3' FROM DUAL UNION ALL
SELECT 'HIJ', 4, 2, 'D4', 'P4' FROM DUAL;
Query 1:
SELECT CONNECT_BY_ROOT( LineItem_Name) AS LineItem_Level1,
CASE LEVEL
WHEN 3 THEN PRIOR LineItem_Name
WHEN 2 THEN LineItem_Name
END AS LineItem_Level2,
CASE LEVEL
WHEN 3 THEN LineItem_Name
END AS LineItem_Level3,
dept_name,
product_name
FROM table_name
START WITH parent_id IS NULL
CONNECT BY PRIOR LineItem_ID = parent_id
Results:
| LINEITEM_LEVEL1 | LINEITEM_LEVEL2 | LINEITEM_LEVEL3 | DEPT_NAME | PRODUCT_NAME |
|-----------------|-----------------|-----------------|-----------|--------------|
| ABC | (null) | (null) | D1 | P1 |
| ABC | CDF | (null) | D2 | P2 |
| ABC | CDF | HIJ | D4 | P4 |
| ABC | EFG | (null) | D3 | P3 |
Query 2: This is an alternative using recursive sub-query factoring which will get the grandparent and parent of the current line item; which is slightly different to the previous query but for 3 levels would give you the same result.
WITH tree ( id, grandparent, parent, item, dept_name, product_name ) AS (
SELECT LineItem_id,
NULL,
NULL,
LineItem_name,
dept_name,
product_name
FROM table_name
WHERE parent_id IS NULL
UNION ALL
SELECT t.lineItem_id,
p.parent,
p.item,
t.lineItem_name,
t.dept_name,
t.product_name
FROM tree p
INNER JOIN
table_name t
ON ( p.id = t.parent_id )
)
SELECT COALESCE( grandparent, parent, item ) AS LineItem_Level1,
CASE
WHEN parent IS NULL THEN NULL
WHEN grandparent IS NULL THEN item
ELSE parent
END AS LineItem_Level2,
NVL2( grandparent, item, NULL ) AS LineItem_Level3,
dept_name,
product_name
FROM tree
Results:
| LINEITEM_LEVEL1 | LINEITEM_LEVEL2 | LINEITEM_LEVEL3 | DEPT_NAME | PRODUCT_NAME |
|-----------------|-----------------|-----------------|-----------|--------------|
| ABC | (null) | (null) | D1 | P1 |
| ABC | CDF | (null) | D2 | P2 |
| ABC | EFG | (null) | D3 | P3 |
| ABC | CDF | HIJ | D4 | P4 |

Related

Hierarchical query with some joins

I am struggling to write a performing query which would consist of the data from one sub-select, and hierarchically retrieved data from another table based on the rows from that first sub-select.
So, I have some data retrieved from multiple tables with joins, which finally boils down to the following:
CREATE TABLE TBL1 (UUID, MiscData, KeyToLookup, ConditionClause ) AS
SELECT 13, 'ATM', 12345, null FROM DUAL UNION ALL
SELECT 447, 'Balance Inquiry', 67890, 'BALANCE_INQUIRY_FEE' FROM DUAL UNION ALL
SELECT 789, 'Credit', 22321, 'CREDIT_FEE' FROM DUAL;
Now, I have another table which stores the hierarchical structure of fees:
CREATE TABLE TBL2 ( TariffDomainID, FeeType, UpperTariffDomainID, ID ) AS
SELECT 1543, 'WHATEVER_FEE', 154, 1 FROM DUAL UNION ALL
SELECT 1543, 'BALANCE_INQUIRY_FEE', 154, 2 FROM DUAL UNION ALL
SELECT 154, 'SMTHELSE_FEE', 15, 3 FROM DUAL UNION ALL
SELECT 154, 'CREDIT_FEE', 15, 4 FROM DUAL UNION ALL
SELECT 15, 'BALANCE_INQUIRY_FEE', null, 5 FROM DUAL;
And there is a way to link the first selection to the lowest row in hierarchy of the second table, there are few joins but finally it's like this:
CREATE TABLE TBL3 ( ID, FirstTblKey, SecondTblKey ) AS
SELECT 1, 67890, 1543 FROM DUAL UNION ALL
SELECT 2, 22321, 1543 FROM DUAL;
The important point is that it's not guaranteed there will be a row with this KeyToLookup directly in the second table, as directed by the TBL3.
E.g. in the example above:
row TBL1.UUID=789 is linked via TBL3 to TBL2 row with TariffDomainID=1543,
but there is no row in TBL2 with TariffDomainID=1543 and FeeType=CREDIT_FEE;
however TBL2 contains a link to the same table but upper level, UpperTariffDomainID=154,
and there is a row in TBL2 with TariffDomainID=154 and FeeType=CREDIT_FEE.
In the end I need to connect the info from TBL1 with the all occurrences of this key in TBL2 hierarchically, numerated by depth of hierarchy.
So I expect to get this:
| UUID | MiscData | KeyToLookup | ConditionClause | TariffDomainIDWithPresence | Depth |
|------|-----------------|-------------|---------------------|----------------------------|-------|
| 13 | ATM | 12345 | null | null | null |
| 447 | Balance Inquiry | 67890 | BALANCE_INQUIRY_FEE | 1543 | 1 |
| 447 | Balance Inquiry | 67890 | BALANCE_INQUIRY_FEE | 15 | 3 |
| 789 | Credit | 22321 | CREDIT_FEE | 154 | 2 |
Could anyone please teach me how to make such a hierarchical query?
You can use a hierarchical query joined to the other two tables:
SELECT DISTINCT
t1.uuid,
t1.miscdata,
t1.keytolookup,
t1.conditionclause,
t2.tariffdomainid,
t2.depth
FROM tbl1 t1
LEFT OUTER JOIN tbl3 t3
ON ( t1.keytolookup = t3.firsttblkey )
OUTER APPLY (
SELECT tariffdomainid,
LEVEL AS depth
FROM tbl2 t2
WHERE t2.tariffdomainid = t3.secondtblkey
START WITH
t2.feetype = t1.conditionclause
CONNECT BY
PRIOR TariffDomainID = UpperTariffDomainID
) t2
ORDER BY
uuid,
depth
Which, for the sample data, outputs:
UUID | MISCDATA | KEYTOLOOKUP | CONDITIONCLAUSE | TARIFFDOMAINID | DEPTH
---: | :-------------- | ----------: | :------------------ | -------------: | ----:
13 | ATM | 12345 | null | null | null
447 | Balance Inquiry | 67890 | BALANCE_INQUIRY_FEE | 1543 | 1
447 | Balance Inquiry | 67890 | BALANCE_INQUIRY_FEE | 1543 | 3
789 | Credit | 22321 | CREDIT_FEE | 1543 | 2
(Note: you need the DISTINCT as there are multiple 1543 and 154 entries in TBL2 so the hierarchical query can take multiple paths to get from the start to the end condition. If your actual data does not have these duplicates then you should be able to remove the DISTINCT clause.)
db<>fiddle here

Filtering a table via another table's values

I have 2 tables:
Value
+----+-------+
| id | name |
+----+-------+
| 1 | Peter |
| 2 | Jane |
| 3 | Joe |
+----+-------+
Filter
+----+---------+------+
| id | valueid | type |
+----+---------+------+
| 1 | 1 | A |
| 2 | 1 | B |
| 3 | 1 | C |
| 4 | 1 | D |
| 5 | 2 | A |
| 6 | 2 | C |
| 7 | 2 | E |
| 8 | 3 | A |
| 9 | 3 | D |
+----+---------+------+
I need to retrieve the values from the Value table where the related Filter table does not contain the type 'B' or 'C'
So in this quick example this would be only Joe.
Please note this is a DB2 DB and i have limited permissions to run selects only.
Or also a NOT IN (<*fullselect*) predicate:
Only that my result is 'Joe', not 'Jane' - and the data constellation would point to that ...
WITH
-- your input, sans reserved words
val(id,nam) AS (
SELECT 1,'Peter' FROM sysibm.sysdummy1
UNION ALL SELECT 2,'Jane' FROM sysibm.sysdummy1
UNION ALL SELECT 3,'Joe' FROM sysibm.sysdummy1
)
,
filtr(id,valueid,typ) AS (
SELECT 1,1,'A' FROM sysibm.sysdummy1
UNION ALL SELECT 2,1,'B' FROM sysibm.sysdummy1
UNION ALL SELECT 3,1,'C' FROM sysibm.sysdummy1
UNION ALL SELECT 4,1,'D' FROM sysibm.sysdummy1
UNION ALL SELECT 5,2,'A' FROM sysibm.sysdummy1
UNION ALL SELECT 6,2,'C' FROM sysibm.sysdummy1
UNION ALL SELECT 7,2,'E' FROM sysibm.sysdummy1
UNION ALL SELECT 8,3,'A' FROM sysibm.sysdummy1
UNION ALL SELECT 9,3,'D' FROM sysibm.sysdummy1
)
-- real query starts here
SELECT
*
FROM val
WHERE id NOT IN (
SELECT valueid FROM filtr WHERE typ IN ('B','C')
)
;
-- out id | nam
-- out ----+-------
-- out 3 | Joe
Or also, a failing left join:
SELECT
val.*
FROM val
LEFT JOIN (
SELECT valueid FROM filtr WHERE typ IN ('B','C')
) filtr
ON filtr.valueid = val.id
WHERE valueid IS NULL
You can use EXISTS, as in:
select *
from value v
where not exists (
select null from filter f
where f.valueid = v.id and f.type in ('B', 'C')
);
Result:
ID NAME
--- -----
3 Joe
See running example at db<>fiddle.

Conditional operations without using SWITCH CASE

I have a couple of complex tables. But their mapping is something like:
TABLE_A:
_________________________________
| LINK_ID | TYPE_ID |
_________________________________
| adfasdnf23454 | TYPE 1 |
| 43fsdfsdcxsh7 | TYPE 1 |
| dfkng037sdfbd | TYPE 1 |
| sd09734fdfhsf | TYPE 2 |
| khsadf94u5dfc | TYPE 2 |
| piukvih66wfa8 | TYPE 3 |
_________________________________
TABLE_B:
_____________________________________________
| LINK_ID | CODE_ID | CODE_VALUE |
_____________________________________________
| adfasdnf23454 | 7 | test 1 |
| fgubk65esdfj7 | 6 | test 2 |
| ooogfsg354fds | 7 | test 3 |
| sd09734fdfhsf | 5 | test 4 |
_____________________________________________
The LINK_ID column links these two tables.
My requirement is to have all the records from TABLE_A checked whether they have a specific CODE_ID or not.
If the record has CODE_ID as 7 - populate CODE_VALUE in a column.
If the record has CODE_ID as 6 - populate CODE_VALUE in another column.
If the record doesn't have a CODE_ID show CODE_VALUE as null.
The catch is, TABLE_B may have records that TABLE_A don't. But the final result should contain the records of TABLE_A alone.
PS: SWITCH CASE not suggested since I would require the fields in the same row. (Please see the multiple rows for same LINK_ID in OBTAINED RESULT on using SWITCH CASE)
OBTAINED RESULT:
_______________________________________________
| LINK_ID | CODE_VALUE_1 | CODE_VALUE_1 |
_______________________________________________
| adfasdnf23454 | test 1 | null |
| adfasdnf23454 | null | test 4 |
| sd09734fdfhsf | test 6 | null |
_______________________________________________
EXPECTED RESULT:
__________________________________________________
| LINK_ID | CODE_VALUE_1 | CODE_VALUE_2 |
__________________________________________________
| adfasdnf23454 | test 1 | test 4 |
| 43fsdfsdcxsh7 | null | null |
| dfkng037sdfbd | null | null |
| sd09734fdfhsf | test 6 | null |
| khsadf94u5dfc | null | null |
| piukvih66wfa8 | null | null |
__________________________________________________
Can someone help on this?
One option uses two correlated subqueries:
select
a.link_id,
(select code_value from table_b b where b.link_id = a.link_id and b.code_id = 7) code_value_1,
(select code_value from table_b b where b.link_id = a.link_id and b.code_id = 6) code_value_2
from table_a a
Note that this assumes no duplicate (link_id, code_id) in table_b. You could also write this with two left joins - which is quite the same logic.
Another solution is a single left join, then conditional aggregation:
select
a.link_id,
max(case when b.code_id = 7 then b.code_value end) code_value_1,
max(case when b.code_id = 6 then b.code_value end) code_value_2
from table_a a
left join table_b b on b.link_id = a.link_id and b.code_id in (6, 7)
group by a.link_id
Problematic part of your question is what to do if two entries in B have same link_id and type_id. You can use min, max, last entry (but for that you need ordering column in B). Or you can list them all:
select *
from a left join b using (link_id)
pivot (listagg(code_value, ', ') within group (order by code_value)
for code_id in (6 as code6, 7 as code7))
Data:
create table a (link_id, type_id) as (
select 'a', 'TYPE 1' from dual union all
select 'b', 'TYPE 1' from dual union all
select 'c', 'TYPE 1' from dual union all
select 'd', 'TYPE 2' from dual );
create table b(LINK_ID, CODE_ID, CODE_VALUE) as (
select 'a', 6, 'test 1' from dual union all
select 'a', 7, 'test 2' from dual union all
select 'a', 7, 'test 3' from dual union all
select 'b', 7, 'test 4' from dual union all
select 'd', 6, 'test 5' from dual );
Result:
LINK_ID TYPE_ID CODE6 CODE7
a TYPE 1 test 1 test 2, test 3
b TYPE 1 test 4
c TYPE 1
d TYPE 2 test 5
dbfiddle

Derive Parent Record from Child records in SQL

I have the following Data which look like below
table
+----------+-----------+-------------+
| Child_ID | Parent_ID | Identifier |
+----------+-----------+-------------+
| C1 | p1 | IN |
| C2 | p1 | OUT |
| C1 | p2 | IN |
| C2 | p2 | OUT |
| C1 | p3 | IN |
| C2 | p3 | OUT |
+----------+-----------+-------------+
I need to output the data in such a way that I can display the parent record in a separate row linking the 2 child id based on the identifier.
Desired Result :
+----+-----------+---------+----------+------------+
| ID | Parent_ID | Child_1 | Child_2 | Identifier |
+----+-----------+---------+----------+------------+
| C1 | P1 | NULL | NULL | IN |
| C2 | P1 | NULL | NULL | OUT |
| P1 | NULL | C1 | C2 | IN |
| C1 | P2 | NULL | NULL | IN |
| C2 | P2 | NULL | NULL | OUT |
| P2 | NULL | C1 | C2 | IN |
+----+-----------+---------+----------+------------+
To achieve This I ran the following query where I tried to left join to separate parent record and then UNION to find child records.
-- Parent
Select c1.PARENT_ID as ID,
Parent_Id,
c1.Child_ID as Child_1
c2.Child_ID as Child_2
c1.Identifier
from sampletable as c1
left join sampletable as c2
on c2.PARENT_ID = c1.PARENT_ID
and c2.Identifier = 'OUT'
where c1.Identifier = 'IN'
UNION
-- CHILD
Select child_id as ID,
Parent_id,
CASE when Identifier = 'IN' then Child_ID
Else NULL END As Child_1,
CASE when Identifier = 'OUT' then Child_ID
Else NULL END As Child_2,
Identifier
from sampletable
where parent_id is not null
Please can someone point out what am i doing wrong here.
Select the children as they are.
For the parents use a subquery in the FROM, to get the set of distinct Parent_IDs. Provided, that there are only two children you can use other subqueries selecting min(Child_ID) or max(Child_ID) respectively, in the column list.
UNION ALL both results.
Put an outer query over the result of the UNION ALL and order it by coalesce(Parent_ID, ID), CASE WHEN ID IS NULL THEN 1 ELSE 0 END, ID to achieve that order you want. The CASE is a hack for ensuring, that IDs being NULL come last. (I'm not sure if NULLS come first or last in SQL Server and too lazy to look it up right now. Or there is an database wide option, if I recall correctly? Anyway, having it explicitly in the query is the safest bet.)
SELECT *
FROM (SELECT Child_ID ID,
Parent_ID,
NULL Child_1,
NULL Child_2,
Identifier
FROM sampletable
UNION ALL
SELECT x.Parent_ID ID,
NULL Parent_ID,
(SELECT min(Child_ID)
FROM sampletable y
WHERE y.Parent_ID = y.Parent_ID) Child_1,
(SELECT max(Child_ID)
FROM sampletable y
WHERE y.Parent_ID = y.Parent_ID) Child_2,
'IN' Identifier
FROM (SELECT DISTINCT Parent_ID
FROM sampletable) x) u
ORDER BY coalesce(Parent_ID,
ID),
CASE
WHEN ID IS NULL
THEN 1
ELSE
0
END,
ID;
SQL Fiddle

Create view from multiple tables, combine values from multiple rows into one row

I have 3 tables as below:
Area table:
UserID | Area
---------------
1 | 10001
2 | 10002
3 | 10003
Info table:
UserID | Info
-----------------
1 | U1_Info1
1 | U1_Info2
1 | U1_Info3
2 | U2_Info1
3 | U3_Info1
Company table:
UserID | Company
-----------------
1 | ComA
2 | ComB
3 | ComC
After that, I want group by UserID. My expected result as below:
UserID | Area | Info1 | Info2 | Info3 | Company
----------------------------------------------------------
1 | 10001 | U1_Info1 | U1_Info2 | U1_Info3 | ComA
2 | 10002 | U2_Info1 | | | ComB
3 | 10003 | U3_Info1 | | | ComC
User 3 doesn't have Info2 and Info3 so I set them = ' '.
Can I make a View like that?
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE Area ( UserID, Area ) AS
SELECT 1, 10001 FROM DUAL
UNION ALL SELECT 2, 10002 FROM DUAL
UNION ALL SELECT 3, 10003 FROM DUAL;
CREATE TABLE Info ( UserID, Info ) AS
SELECT 1, 'U1_Info1' FROM DUAL
UNION ALL SELECT 1, 'U1_Info2' FROM DUAL
UNION ALL SELECT 1, 'U1_Info3' FROM DUAL
UNION ALL SELECT 2, 'U2_Info1' FROM DUAL
UNION ALL SELECT 3, 'U3_Info1' FROM DUAL;
CREATE TABLE Company (UserID, Company ) AS
SELECT 1, 'ComA' FROM DUAL
UNION ALL SELECT 2, 'ComB' FROM DUAL
UNION ALL SELECT 3, 'ComC' FROM DUAL;
CREATE VIEW TEST AS
SELECT A.UserID,
MAX( A.Area ) AS Area,
MAX( CASE WHEN I.Info LIKE '%_Info1' THEN I.Info END ) AS Info1,
MAX( CASE WHEN I.Info LIKE '%_Info2' THEN I.Info END ) AS Info2,
MAX( CASE WHEN I.Info LIKE '%_Info3' THEN I.Info END ) AS Info3,
MAX( C.Company ) AS Company
FROM Area A
INNER JOIN
Company C
ON ( A.UserID = C.UserID )
LEFT OUTER JOIN
Info I
ON ( A.UserID = I.UserID )
GROUP BY
A.UserID
Query 1:
SELECT * FROM test
Results:
| USERID | AREA | INFO1 | INFO2 | INFO3 | COMPANY |
|--------|-------|----------|----------|----------|---------|
| 1 | 10001 | U1_Info1 | U1_Info2 | U1_Info3 | ComA |
| 2 | 10002 | U2_Info1 | (null) | (null) | ComB |
| 3 | 10003 | U3_Info1 | (null) | (null) | ComC |