compare user supplied list against table, with nulls for non-matching rows - sql

Given a list of user-supplied numbers, which I'll refer to as myList, I want to find out which ones have a match against table MasterList, and which ones are null (no match)
so, given db contents
MasterList
----------
ID Number
1 3333333
2 4444444
3 5555555
If myList is ['1111111','2222222','3333333','4444444']
I want the following output:
1111111, null
2222222, null
3333333, 1
4444444, 2
Ideas I've tried:
This, of course, yields only the ones that match.
select Number, ID
from MasterList
where Number in('1111111','2222222','3333333','4444444')
My next idea is no more helpful:
select temp.Number, master.Number
from MasterList master
left join MasterList temp
on master.id=temp.id
and temp.Number in('1111111','2222222','3333333','4444444')
If the list were itself a table temp, it would be trivial to get the desired output:
select temp.number, master.id
from temp -- (ie, the list, where temp.number is the given list)
left join master on master.number=temp.number
-- optional where
where temp.number in('1111111','2222222','3333333','4444444')
This idea never materialized:
select temp.number, master.id
from (select '1111111','2222222','3333333','4444444') temp
left join master on master.number on....
Can this be done without a temporary table?
If not, how do you make a temporary table in DB2? (IBM documentation is helpful if you already know how to do it...)

You want an outer join, here a left outer join (we want all the rows from the left and any rows on the right that match the join condition), as you rightly say in the question. Here I'm using a Common Table Expression (CTE) to basically create a temp table on the fly.
WITH inlist (num) as ( VALUES
(111111),
(222222),
(333333),
(444444) )
SELECT num, id
FROM inlist LEFT OUTER JOIN masterlist
ON masterlist.number = inlist.num
ORDER BY num;
That yields:
NUM ID
----------- -----------
111111 -
222222 -
333333 1
444444 2
4 record(s) selected.

I'm not super-familiar with DB2 (haven't written SQL for that in at least 15 years, and not much back then), so I don't know how much you'll need to edit this to make it work, but I think this will do what you want (Edited SQL to use VALUES clause):
SELECT
my.Number1,
CASE WHEN ml.Number1 IS NULL
THEN NULL
ELSE ROW_NUMBER() OVER (ORDER BY my.Number1) END AS Indicator
FROM
(VALUES ('1111111'),('2222222'),('3333333'),('4444444'))
AS my(Number1)
LEFT JOIN
MasterList AS ml
ON
my.Number1 = ml.Number1;

Related

Is it optimal to use multiple joins in update query?

My update query checks whether the column “Houses” is null in any of the rows in my source table by joining the id between the target & source table (check query one). The column Houses being null in this case indicates that the row has expired; thus, I need to expire the row id in my target table and set the expired date. The query works fine, but I was wondering if it can be improved; I'm new to SQL, so I don't know if using two joins is the best way to accomplish the result I want. My update query will later be used against millions of rows. No columns has been indexed yet.
Query:
(Query one)
Update t
set valid_date = GETDATE()
From Target T
JOIN SOURCE S ON S.ID = T.ID
LEFT JOIN SOURCE S2 ON S2.Houses = t.Houses
WHERE S2.Houses is null
Target:
ID
namn
middlename
Houses
date
1
demo
hello
2
null
2
demo2
test
4
null
3
demo3
test1
5
null
Source:
ID
namn
middlename
Houses
1
demo
hello
null
3
demo
world
null
Expected output after running update query :
ID
namn
middlename
Houses
date
1
demo
hello
2
2022-12-06
2
demo2
test
4
null
3
demo3
test1
5
2022-12-06
I would recommend exists:
update t
set valid_date = getdate()
from target t
where exists (select 1 from source s where s.id = t.id and s.houses is null)
Note that your original query does not exactly do what you want. It cannot distinguish source rows that do not exist from source rows that exist and whose houses column is null. In your example, it would update row 2, which is not what you seem to want. You would need an INNER JOIN instead of the LEFT JOIN.
With EXISTS, you want an index on source(id, houses) so the subquery can execute efficiently against target rows. This index is probably worthwhile for the the JOIN as well.
I don't see why you'd need to join on the column houses at all.
Find all rows in source that have value NULL in the column houses.
Then update all rows in target that have the IDs of the source rows.
I prefer to write these kind of complex updates using CTEs. It looks more readable to me.
WITH
CTE
AS
(
SELECT
Target.ID
,Target.Date
FROM
Source
INNER JOIN Target ON Target.ID = Source.ID
WHERE Source.Houses IS NULL
)
UPDATE CTE
SET Date = GETDATE();
To efficiently find rows in source that have value NULL in the column houses you should create an index, something like this:
CREATE INDEX IX_Houses ON Source
(
Houses
);
I assume that ID is a primary key with a clustered unique index, so ID would be included in the IX_Houses index implicitly.

Get the list for Super and sub types

Am Having the Tables in SQL server as Super and Sub types like below. Now if i have to get list of Furnitures then how can i get the list?
Furniture table:
Id FurnituretypeId NoofLegs
-------------------------------
1 1 4
2 2 4
FurnitureType table:
Id Name
-----------------
1 chair
2 cot
3 table
Chair Table:
Id Name CansSwing CanDetachable FurnitureId
------------------------------------------------------------
1 Chair1 Y Y 1
Cot Table:
Id Name CotType Storage StorageType FurnitureId
-------------------------------------------------------------------
1 Cot1 Auto Y Drawer 2
How can i get the entire furniture list as some of them are chair and some of them are cot. How can i join the these tables with furniture table and get the list?
Hmmm . . . union all and join?
select cc.*, f.*
from ((select Id, Name, CansSwing, CanDetachable,
NULL as CotType, NULL as Storage, NULL as StorageType, FurnitureId
from chairs
) union all
(select Id, Name, NULL as CansSwing, NULL as CanDetachable,
CotType, Storage, StorageType, FurnitureId
from cots
)
) cc join
furniture f
on cc.furnitureid = f.id;
This is a classical learning problem, that's why I'm not giving you the code to solve this but all the insights you need to do so.
You have multiple approaches possible, but I'm describing two simple ones:
1) Use the UNION statement to join two separate queries one for Chair and the other for Cot, bare in mind that both SELECT have to return the same structure.
SELECT
a1,
a2,
etc..
FROM table1 a1
JOIN table2 a2 ON a1.some = a2.some
UNION
SELECT
a1,
a3,
etc..
FROM table1 a1
JOIN table3 a3 ON a1.some = a3.some
2) You can do it all in one SELECT statement using a LEFT JOIN to both tables and and in the select using COALESCE or ISNULL to get the values for one table or the other. In the WHERE condition you have to force one or the other join to be not null.
SELECT
a1,
COALESCE(a2,a3) as col2
FROM table1
LEFT JOIN table2 a2 ON a1.some = a2.some
LEFT JOIN table3 a3 ON a1.some = a3.some
WHERE
a2.some IS NOT NULL
OR a3.some IS NOT NULL
Mapping objects into relational models takes a degree of understanding of what is possible vs. what is wise in an RDBMS. Object oriented database systems tried to go after problems like this (generally without much success) precisely because the problem statement is arguably not the right one.
Please consider just putting all of these in one table. Then use null for the fields that don't really matter for each sub-type. You will likely end up being a lot happier in the end since you can spend less time at runtime doing joins and instead just query the information you need and use indexing on the same table to find the fasted path for each query you want to run.
SELECT * FROM CombinedTable;

Oracle 11 SQL lists and two table comparison

I have a list of 12 strings (strings of numbers) that I need to compare to an existing table in Oracle. However I don't want to create a table just to do the compare; that takes more time than it is worth.
select
column_value as account_number
from
table(sys.odcivarchar2list('27001', '900480', '589358', '130740',
'807958', '579813', '1000100462', '656025',
'11046', '945287', '18193', '897603'))
This provides the correct result.
Now I want to compare that list of 12 against an actual table with account numbers to find the missing values. Normally with two tables I would do a left join table1.account_number = table2.account_number and table two results will have blanks. When I attempt that using the above, all I get are the results where the two records are equal.
select column_value as account_number, k.acct_num
from table(sys.odcivarchar2list('27001',
'900480',
'589358',
'130740',
'807958',
'579813',
'1000100462',
'656025',
'11046',
'945287',
'18193',
'897603'
)) left join
isi_owner.t_known_acct k on column_value = k.acct_num
9 match, but 3 should be included in table1 and blank in table2
Thoughts?
Sean

SQL - include results you are looking for in a column and set all other values to null

I have two tables, one with orders and another with order comments. I want to join these two tables. They are joined on a column "EID" which exists in both tables. I want all orders. I also want to see all comments with only certain criteria AND all other comments should be set to null. How do I go about this?
Orders Table
Order_Number
1
2
3
4
Comments Table
Comments
Cancelled On
Ordered On
Cancelled On
Cancelled On
In this example I would like to see for my results:
Order_Number | Comments
1 | Cancelled On
2 | Null
3 | Cancelled On
4 | Cancelled On
Thanks!
This seems like a rather trivial left join.
select o.order_number, c.comments
from orders o
left join comments c
on o.eid = c.eid
and (here goes your criteria for comments)
Tested on Oracle, there might be subtle syntax differences for other DB engines.
It depends on one condition:
Are you trying to SET the other comments to null? (replace the values in the table)
or
Are you trying to DISPLAY the other comments as null? (dont display them)
If you want to change the values in the table use
UPDATE `table` SET `column` = null WHERE condition;
otherwise use:
SELECT column FROM table JOIN othertable WHERE condition;

Simple SQL Select from 2 Tables (What is a Join?)

I'm new to SQL. I have a simple problem with getting the results from two different tables.
I have two tables in a database. The first table has a column with an id reference, which corresponds to rows in the second table. What SELECT do I need to perform to get a result such that the ids are repalced by all of the values in the second table. To visualize the tables I am discussing:
TABLE_USERS
===========
id username group
-- -------- -----
1 jim A
2 alice A
3 brandon B
TABLE_GROUPS
============
id groupname members
-- --------- -------
A designer 134
B photographer 39
DESIRED_SELECTION
=================
id username group
-- -------- -----
1 jim designer
2 alice designer
3 brandon photographer
Thanks!
You do, in fact, want to JOIN the two tables:
SELECT * FROM
TABLE_USERS LEFT JOIN TABLE_GROUPS
ON TABLE_USERS.group = TABLE_GROUPS.id
The trick of joining tables is to find the values that must match in the two tables, and use the on to tell SQL to match them. This table has a ID column to let you do that = you will join the table, ON, and then list the values that need to be equal.
If you do not want all of the columns in both tables, you can simply list only the columns you need in your final query. This means that instead of Select *, you list the columns you want. As shown below, if a column appears with the same name in both tables, you need to prepend the table name, so that SQL know which value you want.
SELECT TABLE_USERS.ID, Username, Groupname
FROM TABLE_USERS
LEFT JOIN TABLE_GROUPS
ON TABLE_USERS.group = TABLE_GROUPS.id
You want a JOIN:
SELECT
u.id,
username,
groupname
FROM
TABLE_USERS AS u
LEFT JOIN TABLE_GROUPS AS g
ON u.group = g.id