SQL - Joining a matrix based on criteria - sql

Hoping someone can help me out here as I appear to being going around in circles as SQL isn't my strong point.
I'm at a loss if I can solve the problem with a join but I suspect not.
Problem:
Essentially I have a table and I need to match codes against a lookup table to see which codes are valid and which are not.
Table: tblMatrix
ID family0 family1 family2 family3 family4
1000 AAALL ZZZAA 11111 NULL NULL
1001 AAALL ZZZAA 11122 NULL NULL
1002 AAALL ZZZBB 11111 NULL NULL
1003 AAALL ZZZBB 11122 NULL NULL
1004 AAARR ZZZAA 11111 NULL NULL
1005 AAARR ZZZAA 11122 XXX11 NULL
1006 AAARR ZZZBB 11111 NULL NULL
1007 AAARR ZZZBB 11122 NULL NULL
The comparison is against the following table:
tblComparision
ID code1 code2 code3 code4
4034 AAALL 11122 NULL NULL
5555 AAARR ZZZAA NULL NULL
6667 11122 AAARR
In a stored procedure, I am looping each row in the tblMatrix and essentially need to a count of if any combination of the codes exist in tblComparison.
So for example, ID 1000 is valid as no combation exists. However, ID 1001 is not valid as both AAALL and 11122 have occurred.
Note also that the codes may not be in the correct order so ID 1007 would not be valid as both AAARR and 11122 exist.
Any thoughts as to how I would achieve this. Would several joins work?
The closest I've got to it working is below. Essentially, it starts to fail when the I family1 isn't a match but not sure how to avoid this.
select * from tblMatrix2 as t1
join tblComparison as t2
on
(t1.family0 = t2.code1 or t1.family0 = t2.code2 or t1.family0 = t2.code3 )
join tblComparison as t3
on
(t1.family1 = t3.code1 or t1.family1 = t3.code2 or t1.family1 = t3.code3 )
join tblComparison as t4
on
(t1.family2 = t4.code1 or t1.family2 = t4.code2 or t1.family2 = t4.code3 )
where t1.id = 10001;
Any help would or pointers would be greatly appreciated!

You can list all values by making a union of the fields (no match will not be shown here):
SELECT sub.id,
CASE WHEN MAX (sub.cn) = 1 THEN 'Valid' ELSE 'Invalid' END status
FROM ( SELECT fam.id, COUNT (lst.id) cn
FROM (SELECT id, family0 family FROM tblMatrix
UNION ALL
SELECT id, family1 FROM tblMatrix
UNION ALL
SELECT id, family2 FROM tblMatrix
UNION ALL
SELECT id, family3 FROM tblMatrix
UNION ALL
SELECT id, family4 FROM tblMatrix) fam
INNER JOIN
(SELECT id, code1 code FROM tblComparison
UNION
SELECT id, code2 FROM tblComparison
UNION
SELECT id, code3 FROM tblComparison
UNION
SELECT id, code4 FROM tblComparison) lst
ON fam.family = lst.code
WHERE fam.family IS NOT NULL AND lst.code IS NOT NULL
GROUP BY fam.id, lst.id) sub
GROUP BY sub.id
ORDER BY id
Bonus info:
These union'ed lists transform your tables to something managable in sql with no repeating columns (like code1,code2 etc.). You could simply change the tables accordingly to have a better data model (whithout NULL):
create table tblMatrix (id int, family varchar2(5));
create table tblComparison (id int, code varchar2(5));
If you need to make family or code groups, just add a column for that.

Related

Query to update all the null values to not null for all the columns in a single or dynamic query

I am looking for a SQL query to update all the columns in table A which has a NULL value to NOT NULL values from table C using reference as table B. All the columns in table A should be updated using a single query or dynamic query. Values should be updated based on column name and values should not be hard coded.
Table A
ID Name zip city Mdate
10001 Sarah NULL NULL 02-20-2018
98765 AJ NULL Ohio 01-15-2018
12345 NULL 7511 Texas 03-08-2018
Table B
ID RollNo Cdate
12345 1 01-18-2018
12345 2 01-22-2018
12345 3 03-20-2018
98765 9 01-18-2018
98765 8 01-22-2018
98765 7 03-20-2018
10001 10 03-08-2018
10001 11 01-15-2018
10001 12 02-20-2018
TABLE C
Rollno Name Zip City Mdate Cdate Modifyby
1 NULL 7511 Texas 01-18-2018 02-02-2017 #John
2 John 5001 NULL 01-22-2018 11-01-2017 #Krish
3 NULL 7000 Ohio 03-20-2018 11-15-2017 #Maria
10 Sarah NULL NULL 03-08-2018 10-05-2017 #tom
11 Tom NULL NULL 01-15-2018 04-05-2017 #Sony
12 NULL 5500 Pune 02-20-2018 03-25-2017 #Sandy
9 AJ NULL Ohio 01-18-2018 10-07-2017 #Mandy
8 NULL NULL NULL 01-12-2018 11-02-2017 #Deck
7 NUll 8000 NULL 01-12-2018 12-05-2017 #Ant
Output:
ID Name zip city Mdate Cdate Modifyby
10001 Sarah 5500 Pune 02-20-2018 03-25-2017 #Sandy
98765 AJ 8000 Ohio 01-18-2018 10-07-2017 #Mandy
12345 John 7511 Texas 03-08-2018 10-05-2017 #tom
In the output, all the NULL column values in table A are updated with NOT NULL values from table C. Please provide a single query to update all the NULL columns in a single query itself.
I am trying below query but unable to update the table with max Function. Is there any alternative for it.
Select * into #temp FROM
(
SELECT A.ID,
Row_number() over(partition by A.ID order by A.mdate desc) as
RNK,C.NAME,C.ZIP,C.CITY,C.MDate,C.Cdate,C.Modifyby
FROM tableA A
INNER JOIN tableB B ON A.ID = B.ID
INNER JOIN tableC C ON B.RollNo = C.RollNo
)X where RNK = 1
UPDATE A SET Name =
CASE
WHEN MAX(A.Name) IS NULL THEN MAX(C.Name)
ELSE Max(A.Name) END
, Zip =
CASE
WHEN MAX(A.Zip) IS NULL THEN Max(C.Zip)
ELSE MAX(A.Zip)
END ,
City =CASE
WHEN MAX(A.City) IS NULL THEN Max(C.City)
ELSE MAX(A.City)
END
FROM #temp A
INNER JOIN TableB B ON A.ID = B.ID
INNER JOIN tablec C ON B.RollNo = C.RollNo
Getting an error:
An aggregate may not appear in the set list of an UPDATE statement.
I need all the values in output table along with null values updated with not null values.
Try this below script-
DEMO HERE
SELECT A.ID,
CASE
WHEN MAX(A.Name) IS NULL THEN MAX(C.Name)
ELSE Max(A.Name)
END Name,
CASE
WHEN MAX(A.Zip) IS NULL THEN Max(C.Zip)
ELSE MAX(A.Zip)
END Zip,
CASE
WHEN MAX(A.City) IS NULL THEN Max(C.City)
ELSE MAX(A.City)
END City,
A.MDate
FROM table_a A
INNER JOIN Table_B B ON A.ID = B.ID
INNER JOIN table_c C ON B.RollNo = C.RollNo
GROUP BY A.ID,A.MDate

Joining two tables on column A or column B in SQL

I have two tables called Plan and Actual. Every row in each table represents a unique item, and I need to find items that are in the Plan table, but not the Actual table, and vice versa.
There are three columns that uniquely identify each item, and the value for each of these columns may or may not be null.
For Example:
Say "Plan" looks like this:
ID_1 ID_2 ID_3
aaa Null Null
Null 111 Null
Null Null 123
bbb 222 Null
ccc Null 456
Null 333 789
ddd 444 202
Say "Actual" looks like this:
ID_1 ID_2 ID_3
aaa Null Null
Null 111 Null
bbb 222 Null
Null 333 789
Null 555 Null
eee Null 303
Using SQL, how can I identify the "In plan not in actual" rows of:
Null Null 123
ccc Null 456
ddd 444 202
And in "In actual not in plan" rows of:
Null 555 Null
eee Null 303
Thank you for your help!
If you are using ORACLE, you can use the operator MINUS. It will select rows in table "Plan" not in table "Actual".
SELECT ID_1, ID_2, ID_3
FROM Plan
MINUS
SELECT ID_1, ID_2, ID_3
FROM Actual;
If you are using SQL Server; use EXCEPT instead of minus.
You can use the LEFT JOIN. Below is the example with MySql.
In plan not in actual
SELECT Plan.* FROM Plan
LEFT JOIN Actual ON (Plan.ID_1 = Actual.ID_1 AND Plan.ID_2 = Actual.ID_2 AND Plan.ID_3 = Actual.ID_3)
WHERE Actual.ID_1 IS NULL AND Actual.ID_2 IS NULL AND Actual.ID_3 IS NULL;
In actual not in plan
SELECT Actual.* FROM Actual
LEFT JOIN Plan ON (Plan.ID_1 = Actual.ID_1 AND Plan.ID_2 = Actual.ID_2 AND Plan.ID_3 = Actual.ID_3)
WHERE Plan.ID_1 IS NULL AND Plan.ID_2 IS NULL AND Plan.ID_3 IS NULL;

Update a null value cell with an ID and the table is related to 3 others

I have a Table that is joined from other tables. I want to update a null value in this Table by a specific number series. Below is the illustration:
The code for the Table which is called List_Codes
SELECT mlk.MLK_CODE
FROM zpt
LEFT OUTER JOIN mes ON mes.ZPT_ID = zpt.ZPT_ID
LEFT OUTER JOIN zmlk ON zpt.ZPT_ID = zmlk.ZPT_ID
LEFT OUTER JOIN mlk ON zmlk.MLK_ID = mlk.MLK_ID
WHERE zpt.zpt_id IS NOT NULL
and zpt.zpt_meteringcode = '123456'
ORDER BY mes.MES_STATUS DESC
Now I want to update this specific row's mlk.MLK_CODE from null to '789'. I have located this row based on the zpt.zpt_meteringcode. Any suggestions plz?
The tables look like this, and the List_Code Table is the result of the above code
Mlk Table
Mlk_id Mlk_code
1 123
2 456
Zpt Table
Zpt_id Zpt_meteringcode
10 123456
20 987654
30 654321
40 147852
Zmlk Table
Zpt_id Mlk_id
20 1
30 2
List_Code Table
Zpt_id Zpt_meteringcode Mlk_id Mlk_code
10 123456
20 987654 1 123
30 654321 2 456
40 147852
I think you need two inserts and one update statements like this.
INSERT INTO mlk (mlk_code)
VALUES ( '789' ); -- Primary key is generated
.....
INSERT INTO zmlk (zpt_id,
mlk_id)
SELECT (SELECT zpt_id
FROM zpt
WHERE zpt_meteringcode = '123456'),
(SELECT mlk_id
FROM mlk
WHERE mlk_code = '789')
FROM dual;
....
UPDATE list_code
SET ( mlk_id, mlk_code ) = (SELECT mlk_id,
mlk_code
FROM mlk
WHERE mlk_code = '789')
WHERE zpt_meteringcode = '123456';

SQL Server two tables with same columns but pick data with no null

I have below sample data, two temp tables with same structure, but I need to get one table output, with taking best values between two tables (best values here is NO NULL's). Ignore null values between two same columns in two tables.
Table 1
Prog OrderNO ORDERKey OrigType REBNO REBACC
A 1 1234 FACILITY NULL NULL
A 2 1234 FACILITY NULL NULL
A 3 1234 FACILITY NULL NULL
Table 2
Prog OrderNO ORDERKey OrigType REBNO REBACC
A 1 NULL NULL 1234 456
A 2 NULL NULL 1234 456
A 3 NULL NULL 1234 456
OUTPUT
Prog OrderNO ORDERKey OrigType REBNO REBACC
A 1 1234 FACILITY 1234 456
A 2 1234 FACILITY 1234 456
A 3 1234 FACILITY 1234 456
Use COALESCE() combined with a FULL OUTER JOIN:
SELECT
COALESCE( Table1.Prog, Table2.Prog ) AS Prog,
COALESCE( Table1.OrderNo, Table2.OrderNo ) AS OrderNo,
COALESCE( Table1.OrderKey, Table2.OrderKey) AS OrderKey,
COALESCE( Table1.OrigType, Table2.OrigType ) AS OrigType,
COALESCE( Table1.RebNo, Table2.RebNo) AS RebNo,
COALESCE( Table1.RebNo, Table2.RebNo) AS RebAcc
FROM
Table1
FULL OUTER JOIN Table2 ON
Table1.Prog = Table2.Prog
AND
Table1.OrderNo = Table2.OrderNo

Mix records of two different tables

I'm looking for a way to solve my SQL problem.
I have 2 tables in Firebird 2.5 ( T1 and T2 ) like these:
T1 (
T1_ID INTEGER,
T1_DAY DATE,
T1_NAME VARCHAR(200)
)
T2 (
T2_ID INTEGER,
T2_DAY DATE,
T2_NAME VARCHAR(200)
)
I need a query that mixes records of those tables and sort them in ascending date order. I don't care if a join query increases the number of fields or the date field is not the same as result or stored procedures are needed.
Example output
T1_ID T1_DAY T1_NAME T2_ID T2_DAY T2_NAME
---------------------------------------------------
1 01/02/2011 BOB NULL NULL NULL
2 27/02/2011 SAM NULL NULL NULL
NULL NULL NULL 8 15/03/2011 PETER
NULL NULL NULL 10 21/03/2011 JOHN
6 17/04/2011 AMY NULL NULL NULL
or (better output)
ID DAY NAME
-------------------------
1 01/02/2011 BOB
2 27/02/2011 SAM
8 15/03/2011 PETER
10 21/03/2011 JOHN
6 17/04/2011 AMY
You want the UNION operator:
SELECT
T1.T1_ID ID,
T1.T1_DAY DAY,
T1.T1_NAME NAME
FROM
T1
UNION
SELECT
T2.T2_ID,
T2.T2_DAY
T2.T2_NAME
FROM
T2
;
You can make the individual selects have any additional features you like. The only restriction is that all of the columns in both of the select lists are in the same order and have the same type (they are "union compatible"). The resulting rows will have column headings like the first select.
edit: To control the ordering of a union, you'll have to do the union in a subselect and the ordering in the outer query.
SELECT u.ID, u.DAY, u.NAME
FROM (
SELECT T1.T1_ID ID, T1.T1_DAY DAY, T1.T1_NAME NAME
FROM T1
UNION
SELECT T2.T2_ID, T2.T2_DAY T2.T2_NAME
FROM T2
) u
ORDER BY u.NAME;