Derive Parent Record from Child records in SQL - 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

Related

Snowflake - Identify duplicate rows and flag them using update statement

I want to identify duplicate rows of a table and add a error code to them. I want to keep one value in all cases and mark all others as duplicate. Snowflake doesn't support CTE & UPDATE statement in one query unlike SQL server. So how do i go about implementing this?
Table creation Code:
DROP TABLE IF EXISTS DUP_CODE_TEST;
CREATE TABLE DUP_CODE_TEST
AS (
SELECT '1' AS PARENT,'OWN' AS REL, '11' AS CHILD, 'ROW1' AS X, NULL AS ERR_CD
UNION ALL
SELECT '1', 'OWN' AS REL, '11' , 'ROW2' , NULL
UNION ALL
SELECT '1', 'OWN' AS REL, '11' , 'ROW3' , NULL
);
Source Table:
+--------+-----+-------+------+--------+
| PARENT | REL | CHILD | X | ERR_CD |
+--------+-----+-------+------+--------+
| 1 | OWN | 11 | ROW1 | NULL |
| 1 | OWN | 11 | ROW2 | NULL |
| 1 | OWN | 11 | ROW3 | NULL |
+--------+-----+-------+------+--------+
I would do this in SQL SERVER
WITH CTE_UPD
AS
(
SELECT *,ROW_NUMBER() OVER (PARTITION BY PARENT,REL,CHILD ORDER BY X ) RN FROM DUP_CODE_TEST
)
UPDATE CTE_UPD
SET ERR_CD = 'AR-DUP'
WHERE RN = 2
and the expected output is
+--------+-----+-------+------+-----------+
| PARENT | REL | CHILD | X | ERR_CD |
+--------+-----+-------+------+-----------+
| 1 | OWN | 11 | ROW1 | NULL |
| 1 | OWN | 11 | ROW2 | DUPLICATE |
| 1 | OWN | 11 | ROW3 | DUPLICATE |
+--------+-----+-------+------+-----------+
You can do something similar -- assuming that X is unique:
UPDATE DUP_CODE_TEST t
SET ERR_CD = 'AR-DUP'
FROM (SELECT PARENT, REL, CHILD, MIN(X) as MIN_X
FROM DUP_CODE_TEST tt
GROUP BY PARENT, REL, CHILD
) tt
WHERE t.PARENT = tt.PARENT AND t.REL = tt.REL AND
t.CHILD = tt.CHILD AND tt.X > t.MIN_X;
That is, Snowflake does support joining to another table (or subquery). This summarizes the table to get a minimum X for each group and then uses that for the update.

Binding together tables using GROUP BY with columns about category

I have a problem with handle aggregate columns in SQL Server 2014 version which does not support GROUP_CONCAT function. My task is to create query which bind together a few tables by its common columns, so suppose there will be two example tables.
Table A (Category 1)
| name | size |
+------+------+
| aaa | 2 |
| bbb | 3 |
Table B (Category 2)
| name | size |
+------+------+
| aaa | 2 |
| ccc | 7 |
Please notice that first records on both tables are the same.
I want to get following results:
| name | size | category_id | secondary_category_id |
+------+------+-------------+-----------------------+
| aaa | 2 | 1 | 2 |
| bbb | 3 | 1 | NULL |
| ccc | 7 | 2 | NULL |
The category_id column is always filled by ID which is hardcoded for each table, for example:
SELECT name, size, '1' AS category_id
FROM Table_A
GROUP BY name, size
UNION ALL
SELECT name, size, '2' AS category_id
FROM Table_B
GROUP BY name, size
But some entries in tables may be duplicated and for those row I want fill secondary_column_id with value identifying table (in this case 2)
This looks like a full join:
select coalesce(c1.name, c2.name) as name,
coalesce(c1.size, c2.size) as size,
(case when c1.name is not null then 1 else 2 end) as category_id,
(case when c1.name is not null and c2.name is not null then 2 end) as secondary_category_id
from category1 c1 full join
category2 c2
on c1.name = c2.name and c1.size = c2.size

How to make 2 columns from one in one select in sqlite?

I've got one database with two columns (id and value). There are two types of values and each id has both of this values. How can I make a select to this database to have three columns in result (id, value1 and value2)
I've tried CASE and GROUP BY, but it shows only one result of each id
Example of a db:
| id | value |
| 0 | a |
| 0 | b |
| 1 | a |
| 1 | b |
Example of the result I am looking for is:
| id | value_a | value_b |
| 0 | a | b |
| 1 | a | b |
UPDATE:
As it was noted in comments, there is too simple data in the example.
The problem is more complicated
An example that would better describe it:
DB:
| id | value | value2 | value3 |
| 0 | a | a2 | a3 |
| 0 | b | b2 | b3 |
| 1 | a | c2 | c3 |
| 1 | b | d2 | d3 |
RESULT:
| id | value_a | value_b | value2_a | value2_b | value3_a | value3_b |
| 0 | a | b | a2 | b2 | a3 | b3 |
| 1 | a | b | c2 | d2 | c3 | d3 |
The output should be sorted by id an have all info from the both rows of each id.
If there are always two values per ID, you can try an aggregation using min() and max().
SELECT id,
min(value) value_a,
max(value) value_b
FROM elbat
GROUP BY id;
select t0.id,t0.Value as Value_A, t1.Value as Value_B
from test t0
inner join test t1 on t0.id = t1.id
where t0.Value = 'a' and t1.value = 'b';
I have used this method to turn "rows" into "columns". Depending on the number of unique values that exist in the table, you may or may not want to use this :)
SELECT id, SUM(CASE WHEN value = "a" then 1 else 0 END) value_a,
SUM(CASE WHEN value = "b" then 1 else 0 END) value_b,
SUM(CASE WHEN value = "c" then 1 else 0 END) value_c,
SUM(CASE WHEN value ="a2" then 1 else 0 END) value_a2,
.
.
.
FROM table
GROUP BY id;
Thanks all for the answers! This is the way how I did this:
WITH a_table AS
(
SELECT id, value, value2, value3 FROM table1 WHERE table1.value = 0
),
b_table AS
(
SELECT id, value, value2, value3 FROM table1 WHERE table1.value = 1
)
SELECT DISTINCT
a_table.id AS id,
a_table.value AS value_a,
a_table.value2 AS value2_a,
a_table.value3 AS value3_a,
b_table.value AS value_b,
b_table.value2 AS value2_b,
b_table.value3 AS value3_b
FROM a_table
JOIN b_table ON a_table.id = b_table.id
GROUP BY id;

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

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 |

SQL to identify duplicates in a tree-like structure

I am looking for a solution for this (MS SQL 2008, btw):
ID | ParentID | Feature_1 | Feature_2
+-----+------------+------------+----------+
1 | NULL | A | B
2 | 1 | A | B
3 | 1 | A | C
4 | 2 | A | C
Whenever a child (a record with a ParentID) has the same set of features (Feature_1 and Feature_2) than its parent, I want to ignore it, essentially not show it in my select *.
So the result set should be
ID | ParentID | Feature_1 | Feature_2
+-----+------------+------------+----------+
1 | NULL | A | B
3 | 1 | A | C
4 | 2 | A | C
Note that ID=2 is dropped, but ID=4 is displayed because it has a different set of features than its parent had.
Any help would be much appreciated!
SELECT
Child.ID,
Child.ParentID,
Child.Feature_1,
Child.Feature_2
FROM
MyTable AS Child
LEFT OUTER JOIN MyTable AS Parent
ON Child.ParentID = Parent.ID
WHERE
Parent.Feature_1 <> Child.Feature_1
OR Parent.Feature_2 <> Child.Feature_2
OR Child.ParentID IS NULL
ORDER BY
Child.ID
SELECT *
FROM table A
WHERE a.ParentID IS NULL OR NOT EXISTS (SELECT 1
FROM table b
WHERE a.ParentID = b.ID
AND a.Feature_1 = b.Feature_1 AND a.Feature_2 = b.Feature_2)