SQL Query to get specific child records - sql

I have a requirement to get child table records based on parent table search criteria but they need to be distinct and output should be like below:
Table A, have three rows. Row one is for generic rules, Row 2 is for specific Category and Row 3 is for Specific Branch, Category and Sub-Category.
Now, my output should consists of the rules which are specific to generic.
Below are the rules for the output:
Input to the query will be Branch, Category and Sub-Category
Each record-set in Table-A is comprised of 03 rows
Row 1 has Branch but Category and Sub-Category as Null
Row 2 has Branch and Category Sub-Category as Null
Row 3 has Branch, Category and Sub-Category.
Each Row in a record-set of Table-A has child records in Table-B
Record with Branch only (Row 1), have generic records and these records can also be child records of Row 2 and Row 3
Record with Branch and Category Sub-Category as Null (Row 2) has child records in Table-B and they are overriding child records of Row 1
Record with Branch, Category and Sub-Category (Row 3) has child records in Table-B and they are overriding child records of Row 1 and Row 2.
All child records of Row 1,2 & 3 will be part of the output but if a child is present in Row 3 then despite if it is present in other Rows output will consists of child record of Row 3
If a child record is present in Row 1 & 2 but not in 3 then output
will have child record of Row 2
if a child record is present in Row 1 but not in Row 2 & 3 then it
will be part of output.
Now,
In the sample output, 'Pay' is present in Row 1,2 and 3 but in the
output we are considering child record of Row 3 as it overrides both Record 1 & 2
'Discount' is present in Record 1 & 3 but output includes child of Row 3
'Items' is not part of Row 1 and Row 2 childs but as it is present in Row 3 so it will be part of output
'Paris' is only part of Row 2 but as it is not overriden by Row 2 so
it is part of output as it is
I have tried following query but it is not giving the required output:
SELECT DISTINCT RULE,
value
FROM siebel.b rxm
WHERE par_row_id IN (SELECT row_id
FROM siebel.a
WHERE ( branch = 'Civil'
AND category = 'C.M.> (Civil)'
AND sub_category IS NULL )
OR ( branch = 'Civil'
AND category = 'C.M. (Civil)'
AND sub_category = 'Pauper' )
OR ( branch = 'Civil'
AND category IS NULL
AND sub_category IS NULL ))
I am using Oracle as RDBMS.
Schema statements:
Create Table A (ROW_ID int, BRANCH varchar(50), CATEGORY varchar(50), SUB_CATEGORY varchar(50))
Create Table B (PAR_ROW_ID int, RULE varchar(50), Value varchar(50))
INSERT INTO A (ROW_ID, BRANCH)
VALUES (1,'Civil')
INSERT INTO A (ROW_ID, BRANCH, CATEGORY)
VALUES (2,'Civil','C.M. (Civil)')
INSERT INTO A (ROW_ID, BRANCH, CATEGORY, SUB_CATEGORY)
VALUES (3,'Civil','C.M. (Civil)','Pauper')
INSERT INTO B (PAR_ROW_ID, RULE, VALUE)
VALUES (1,'Pay','10')
INSERT INTO B (PAR_ROW_ID, RULE, VALUE)
VALUES (1','Days','25')
INSERT INTO B (PAR_ROW_ID, RULE, VALUE)
VALUES (1,'Discount','20')
INSERT INTO B (PAR_ROW_ID, RULE, VALUE)
VALUES (2,'Pairs','5')
INSERT INTO B (PAR_ROW_ID, RULE, VALUE)
VALUES (2,'Pay','30')
INSERT INTO B (PAR_ROW_ID, RULE, VALUE)
VALUES (3,'Pay','15')
INSERT INTO B (PAR_ROW_ID, RULE, VALUE)
VALUES (3,'Discount','20')
INSERT INTO B (PAR_ROW_ID, RULE, VALUE)
VALUES (3,'items','30')

SELECT MAX( par_row_id ) AS par_row_id,
rule,
MAX( value ) KEEP ( DENSE_RANK LAST OVER ORDER BY par_row_id ) AS value
FROM table_b
GROUP BY rule
Or:
SELECT par_row_id,
rule,
value
FROM (
SELECT b.*,
ROW_NUMBER() OVER ( PARTITION BY rule ORDER BY par_row_id DESC ) AS rn
FROM table_b b
)
WHERE rn = 1;

Related

Get last value based on a condition on ordered table in SQL

I have a table representing product groups. Groups are defined by row number and their Type. Hence here we have 2 groups, the group is defined by the Type A, the Type B being components.
Row number
Type
0
A
1
B
2
B
3
A
4
B
5
B
With just this data, I need to find back the row number of the last preceeding Type A occurence :
Row number
Type
Row father
0
A
0
1
B
0
2
B
0
3
A
3
4
B
3
5
B
3
I can't find a way to get this. It's a sort of lag() or first_value() based on condition. Here I have 2 groups of 2 components, but I can have more groups with different sizes. The only thing that defines the group are the row number : every row number under Type A (so with Type B) is a child of the above Type A, until next type A.
Thank you for your help
You can achieve the desired result by using a join in conjunction with a group-by.
Test Data
CREATE TABLE TEST (ROW_NUMBER integer, TYPE varchar(1));
INSERT INTO test VALUES (0, 'A');
INSERT INTO test VALUES (1, 'B');
INSERT INTO test VALUES (2, 'B');
INSERT INTO test VALUES (3, 'A');
INSERT INTO test VALUES (4, 'B');
INSERT INTO test VALUES (5, 'B');
Query
SELECT T.*, MAX(A.ROW_NUMBER) AS ROW_FATHER
FROM TEST T
LEFT JOIN TEST A
ON A.TYPE = 'A' AND a.ROW_NUMBER <= T.ROW_NUMBER
GROUP BY T.ROW_NUMBER, T.TYPE
ORDER BY T.ROW_NUMBER
Result

Is it possible to insert multiple rows in a table based on a select returning more than one row

Using Oracle SQL, I am trying to insert into table A based on select from table B, but I am not sure how to achieve this, since the select is returning more than one row.
INSERT INTO A
VALUES
(
SELECT id FROM B WHERE status = 'APPROVED',
'Hardcoded-Value'
);
Table B:
id
status
1
APPROVED
2
DECLINED
3
APPROVED
Based on that insert, I want to achieve following:
Table A:
Column A
Column B
1
Hardcoded-Value
3
Hardcoded-Value
You can use a const in the select list
INSERT INTO A(colA, colB)
SELECT id, 'Hardcoded-Value'
FROM B
WHERE status = 'APPROVED'

Derive groups of records that match over multiple columns, but where some column values might be NULL

I would like an efficient means of deriving groups of matching records across multiple fields. Let's say I have the following table:
CREATE TABLE cust
(
id INT NOT NULL,
class VARCHAR(1) NULL,
cust_type VARCHAR(1) NULL,
terms VARCHAR(1) NULL
);
INSERT INTO cust
VALUES
(1,'A',NULL,'C'),
(2,NULL,'B','C'),
(3,'A','B',NULL),
(4,NULL,NULL,'C'),
(5,'D','E',NULL),
(6,'D',NULL,NULL);
What I am looking to get is the set of IDs for which matching values unify a set of records over the three fields (class, cust_type and terms), so that I can apply a unique ID to the group.
In the example, records 1-4 constitute one match group over the three fields, while records 5-6 form a separate match.
The following does the job:
SELECT
DISTINCT
a.id,
DENSE_RANK() OVER (ORDER BY max(b.class),max(b.cust_type),max(b.terms)) AS match_group
FROM cust AS a
INNER JOIN
cust AS b
ON
a.class = b.class
OR a.cust_type = b.cust_type
OR a.terms = b.terms
GROUP BY a.id
ORDER BY a.id
id match_group
-- -----------
1 1
2 1
3 1
4 1
5 2
6 2
**But, is there a better way?** Running this query on a table of over a million rows is painful...
As Graham pointed out in the comments, the above query doesn't satisfy the requirements if another record is added that would group all the records together.
The following values should be grouped together in one group:
INSERT INTO cust
VALUES
(1,'A',NULL,'C'),
(2,NULL,'B','C'),
(3,'A','B',NULL),
(4,NULL,NULL,'C'),
(5,'D','E',NULL),
(6,'D',NULL,NULL),
(7,'D','B','C');
Would yield:
id match_group
-- -----------
1 1
2 1
3 1
4 1
5 1
6 1
...because the class value of D groups records 5, 6 and 7. The terms value of C matches records 1, 2 and 4 to that group, and cust_type value B ( or class value A) pulls in record 3.
Hopefully that all makes sense.
I don't think you can do this with a (recursive) Select.
I did something similar (trying to identify unique households) using a temporary table & repeated updates using following logic:
For each class|cust_type|terms get the minimum id and update that temp table:
update temp
from
(
SELECT
class, -- similar for cust_type & terms
min(id) as min_id
from temp
group by class
) x
set id = min_id
where temp.class = x.class
and temp.id <> x.min_id
;
Repeat all three updates until none of them updates a row.

Insert value from two tables based on the rows source table

I have two tables A and B each with 1 column for simplicity sake and they are primary Keys.
A contains the values (1,2,3) B contains (1,2,3)
The third table needs to be the insertion of both A and B and has a composite primary key.
Table C (id, src)
If the id is coming from table A I'd like src to be 'A' and if its coming from B then 'B'.
There can be duplicate ID's between the tables but they are not the same item which is why I need to create a composite key based on which table the row is coming from.
I've tried
Insert into C (anID, src)
Select
Case when (A.anID is not null)
then A.anID else B.anID end,
case when (A.anID is not null)
then 'A' else 'B' end
from
A,
B
But my results always end up as just 3 rows (1, A) (2,A) (3,A) When there should be 6 rows (one of each of those with a B)
insert into TableC (id, src)
select ID, 'A' from tableA
union
select ID, 'B' from tableB

sql query logic

I have following data set
a b c
`1` 2 3
3 6 9
9 2 11
As you can see column a's first value is fixed (i.e. 1), but from second row it picks up the value of column c of previous record.
Column b's values are random and column c's value is calculated as c = a + b
I need to write a sql query which will select this data in above format. I tried writing using lag function but couldn't achieve.
Please help.
Edit :
Column b exists in table only, a and c needs to calculated based on the values of b.
Hanumant
SQL> select a
2 , b
3 , c
4 from dual
5 model
6 dimension by (0 i)
7 measures (0 a, 0 b, 0 c)
8 rules iterate (5)
9 ( a[iteration_number] = nvl(c[iteration_number-1],1)
10 , b[iteration_number] = ceil(dbms_random.value(0,10))
11 , c[iteration_number] = a[iteration_number] + b[iteration_number]
12 )
13 order by i
14 /
A B C
---------- ---------- ----------
1 4 5
5 8 13
13 8 21
21 2 23
23 10 33
5 rows selected.
Regards,
Rob.
Without knowing the relation between the rows ,how can we calculate the sum of the previous row a and b column to current row a column .I have created two more column id and parent in the table to find the relation between the two rows.
parent is the column which tell us about the previous row ,and id is the primary key of the row .
create table test1 (a number ,b number ,c number ,id number ,parent number);
Insert into TEST1 (A, B, C, ID) Values (1, 2, 3, 1);
Insert into TEST1 (B, PARENT, ID) Values (6, 1, 2);
Insert into TEST1 (B, PARENT, ID) Values (4, 2, 3);
WITH recursive (a, b, c,rn) AS
(SELECT a,b,c,id rn
FROM test1
WHERE parent IS NULL
UNION ALL
SELECT (rec.a+ rec.b) a
,t1.b b
,(rec.a+ rec.b+t1.b) c
,t1.id rn
FROM recursive rec,test1 t1
WHERE t1.parent = rec.rn
)
SELECT a,b,c
FROM recursive;
The WITH keyword defines the name recursive for the subquery that is to follow
WITH recursive (a, b, c,rn) AS
Next comes the first part of the named subquery
SELECT a,b,c,id rn
FROM test1
WHERE parent IS NULL
The named subquery is a UNION ALL of two queries. This, the first query, defines the starting point for the recursion. As in my CONNECT BY query, I want to know what is the start with record.
Next up is the part that was most confusing :
SELECT (rec.a+ rec.b) a
,t1.b b
,(rec.a+ rec.b+t1.b) c
,t1.id rn
FROM recursive rec,test1 t1
WHERE t1.parent = rec.rn
This is how it works :
WITH query: 1. The parent query executes:
SELECT a,b,c
FROM recursive;
This triggers execution of the named subquery. 2 The first query in the subquery's union executes, giving us a seed row with which to begin the recursion:
SELECT a,b,c,id rn
FROM test1
WHERE parent IS NULL
The seed row in this case will be for id =1 having parent is null. Let's refer to the seed row from here on out as the "new results", new in the sense that we haven't finished processing them yet.
The second query in the subquery's union executes:
SELECT (rec.a+ rec.b) a
,t1.b b
,(rec.a+ rec.b+t1.b) c
,t1.id rn
FROM recursive rec,test1 t1
WHERE t1.parent = rec.rn