SQL left outer join to find not in 3 tables - sql

I'd like to get a count of items not in 3 tables. The third table is in another database and should only be for producer_ID 139.
Run as this I get 859 as a count:
SELECT count(item.item_ID) as itemcount
FROM item
LEFT OUTER JOIN item_Subject
ON (item.item_ID = item_Subject.item_ID)
LEFT OUTER JOIN item_Category
ON (item.item_ID = item_Category.item_ID)
LEFT OUTER JOIN DATABASE2.dbo.item_SuperCategory
ON (item.item_ID = DATABASE2.dbo.item_SuperCategory.item_ID
and DATABASE2.dbo.item_SuperCategory.Producer_ID = 139)
WHERE item_Category.item_ID IS NULL
and item_Subject.item_ID IS NULL
and DATABASE2.dbo.item_SuperCategory.item_ID IS NULL
But if I take out the DATABASE2.dbo.item_SuperCategory.Producer_ID = 139 and run I get only 23. I expected the number to be reduced, not increased. What is wrong?

This query counts items which have null itemID. The fact that you get 23 as opposed to 859 after removing the producer_id = 139 means that items with a producer_id of 139 account for 836 of your items in the DATABASE2.dbo.item_SuperCategory table.
Remember that you're searching for null values in a left-joined table, so to further filter that table alone in the join clause can only increase nulls.

Related

Create a table containing all rows of 3 tables join with 3 similar columns [SQL]

As the title indicates (in an incorrect way), I would like to create a table bringing together all the lines of 3 different tables, with as common points, their creation date and a folder id (present in each of the 3 tables).
simple schema of the DB : image of the DB schema with relations
How would you write this in SQL ?
PS: it's maybe super easy but I'm all new with SQL, sorry !
PS2: don't hesitate if you need more explanations or precisions
Thank you :)
What I tried :
SELECT product_folder.id AS product_folder_id
pens.brand AS pen_brand
schoolbags.size AS schoolbag_size
notebooks.number_of_pages AS notebook_pages
created_at,
updated_at
FROM product_folders pf
LEFT JOIN product_folders pf ON schoolbags.product_folder_id = pf.id
LEFT JOIN product_folders pf ON pens.product_folder_id = pf.id
LEFT JOIN product_folders pf ON notebooks.product_folder_id = pf.id
But it doesn’t return what I ask (an error).
EDIT :
The error :
'ERROR: table name "pf" specified more than once'
The table should look something like this : image of the table I want to have
More precisely : (*p_f_id = product_folder_id)
------------------------------all-products------------------------------
id p_f_id* brand size price nb_of_pages created_at updated_at
-- ------- ---------- ------- ------ ------------ ----------- ---------
1 34 Watermark NULL 34.00 NULL 12-04-2022 15-04-2022
2 22 NULL medium 40.00 NULL 28-11-2022 29-11-2022
3 18 NULL NULL 12.00 42 06-09-2022 06-09-2022
I know some of the columns will return NULL (ex: pens don't have pages so > NULL) and I'm ok with that.
I just want the column product_folder_id filled with the id of the folder of the different products and the created_at and updated_at columns filled with the date of each rows.
Your first error is that you joined multiple times the table product_folder with the same alias "pf", you cannot do this because if you want to select fields from any of the joined tables, SQL must know which one you want to select from (that's the goal of an alias). You could for example do:
SELECT pf1.id, pf2.id
FROM product_folders pf1
LEFT JOIN product_folders pf2 ON pf2.id = pf1.id
It makes no real sense (it will select twice the same id for each row of product_folders) but shows you that you must use the aliases in the SELECT part and in the ON part to distinguish joined tables.
Your main error though is for your case, you don't need to join on the same table, and then you don't need to give aliases. You want to join directly on the tables you want to select the fields from:
SELECT product_folder.id AS product_folder_id
pens.brand AS pen_brand
schoolbags.size AS schoolbag_size
notebooks.number_of_pages AS notebook_pages
/*created_at,
updated_at*/
FROM product_folders
LEFT JOIN schoolbags ON schoolbags.product_folder_id = product_folders.id
LEFT JOIN pens ON pens.product_folder_id = product_folders.id
LEFT JOIN notebooks ON notebooks.product_folder_id = product_folders.id
I commented created_at, updated_at because from the question we cannot know where you want these columns from, as they don't exist in product_folder. It could be schoolbags.created_at, schoolbags.updated_at or pens.created_at, pens.updated_at or notebooks.created_at, notebooks.updated_at, or all of them...
EDIT FOLLOWING MORE INFOS:
Since you have entries in product_folder that don't correspond to the 3 joined tables, you must filter relevant results with a WHERE.
Additionnally, if each row in product_folder corresponds to only one of the 3 joined tables, you can retreive created_at / updated_at by testing if the id is null (or another field) with IF.
That would give:
SELECT product_folder.id AS product_folder_id,
pens.brand AS pen_brand,
schoolbags.size AS schoolbag_size,
notebooks.number_of_pages AS notebook_pages,
IF(pens.id IS NOT NULL, pens.created_at,
IF(schoolbags.id IS NOT NULL, schoolbags.created_at, notebooks.created_at)
) as created_at,
IF(pens.id IS NOT NULL, pens.updated_at,
IF(schoolbags.id IS NOT NULL, schoolbags.updated_at, notebooks.updated_at)
) as updated_at,
FROM product_folders
LEFT JOIN schoolbags ON schoolbags.product_folder_id = product_folders.id
LEFT JOIN pens ON pens.product_folder_id = product_folders.id
LEFT JOIN notebooks ON notebooks.product_folder_id = product_folders.id
WHERE (pens.brand IS NOT NULL OR schoolbags.size IS NOT NULL OR notebooks.number_of_pages IS NOT NULL)
It is not known, what result you want. Some correction to your query may be help to next step
SELECT
pf.id AS product_folder_id
,pens.brand AS pen_brand
,schoolbags.size AS schoolbag_size
,notebooks.number_of_pages AS notebook_pages
,pf.created_at
,pf.updated_at
FROM product_folders pf
LEFT JOIN product_folders schoolbags ON schoolbags.product_folder_id = pf.id
LEFT JOIN product_folders pens ON pens.product_folder_id = pf.id
LEFT JOIN product_folders notebooks ON notebooks.product_folder_id = pf.id
May be table product_folders has name all_products, as shown on image. Try this
SELECT
pf.id AS product_folder_id
,pens.brand AS pen_brand
,schoolbags.size AS schoolbag_size
,notebooks.number_of_pages AS notebook_pages
,pf.created_at
,pf.updated_at
FROM all_products pf
LEFT JOIN all_products schoolbags ON schoolbags.product_folder_id = pf.id
LEFT JOIN all_products pens ON pens.product_folder_id = pf.id
LEFT JOIN all_products notebooks ON notebooks.product_folder_id = pf.id

how to find the rows not matched by the left join and perform some operations on top of it in sql server?

I'm trying to delete using left join from my sql server studio and my question is how do i get the list of ids that are getting deleted as part of the left join also i would like to compare the difference between the sum from both the tables
Table A:
ID NAME LOC SUM
4 abc NY 500
5 seq CA 100
15 juv TX 120
Table B:
ID NAME LOC SUM INFO
5 seq CA 90 x
18 jay AL 94 x
15 juv CL 190 x
I want to get to the number of rows that are getting removed as part of the left join and i want to see the difference in the sum
DELETE MYDB
FROM MYDB.A
LEFT JOIN MYDB.B
ON A.ID=B.ID
WHERE A.ID=B.ID
It is not clear why you would be using a LEFT JOIN for the JOIN. Your WHERE clause -- which is otherwise redundant -- is turning the outer join into an inner join.
I would suggest using exists:
delete from mydb.a
where exists (select 1 from mydb.b where b.id = a.id);
For a count, you can use:
select count(*)
from mydb.a
where exists (select 1 from mydb.b where b.id = a.id);
Do note: If you run these as two separate operations, the underlying data can change between the operations.
After running the delete, you can use ##ROWCOUNT to get the number of records deleted.

SQL Left Outer join with where clause reduces results from left outer join

I have the following query that works exactly how I would expect it to. It returns back all of the Statuses with the counts.
SELECT
ProcessStatuses.Status,
COUNT(SecretProcesses.ProcessStatusID) AS Count
FROM
ProcessStatuses
LEFT OUTER JOIN
SecretProcesses ON ProcessStatuses.ProcessStatusID = SecretProcesses.ProcessStatusID
GROUP BY
ProcessStatuses.Status
Result:
Status Count
-------------
status1 0
status2 0
status3 0
status4 0
status5 0
status6 1
status7 0
status8 0
However if I add a WHERE clause to the query it returns back only the statuses that have counts.
For example
SELECT
ProcessStatuses.Status,
COUNT(SecretProcesses.ProcessStatusID) AS Count
FROM
ProcessStatuses
LEFT OUTER JOIN
SecretProcesses ON ProcessStatuses.ProcessStatusID = SecretProcesses.ProcessStatusID
WHERE
AreaID IN (21, 22, 23)
GROUP BY
ProcessStatuses.Status
Result:
Status Count
---------------
status6 1
This kind of defeats the purpose of doing a left outer join since I want to be able to filter the results by area that they reside in, while still displaying all of the possible statuses. The where clause is only returning statuses that have values, rather than all of them.
Assuming you still want to return all the statuses and AreaId is in the SecretProcesses table, you need to move the where criteria to the on condition because it is negating your outer join:
SELECT PS.Status, COUNT(SPProcessStatusID) AS Count
FROM ProcessStatuses PS
LEFT JOIN SecretProcesses SP ON PS.ProcessStatusID = SP.ProcessStatusID
AND SP.AreaID IN (21, 22, 23)
GROUP BY PS.Status

LEFT JOIN not returning NULL

I guess the problem comes down to this: what are some extreme scenarios where using a LEFT OUTER JOIN DOES NOT return the values as expected? Because in the result set I'm expecting the fields I joined on (item and ID) + any NULL values where the rows don't match, but item and ID don't show up.
Info:
qry_HersheySAPMaxDate2 returns 95 rows.
qry_HersheySAPMaxDate2 could have NULL values for MaxOfMaxOfjob_Date, SumOfSumOfqty_on_hand, product_code, and whse, whereas ID and item will always have a value.
qry_HersheySAPMaxDate3 returns 85 rows.
qry_HersheySAPMaxDate3 does not have any NULL values in any field, but excludes 10 id and item rows.
The query:
SELECT
qry_HersheySAPMaxDate3.ID,
qry_HersheySAPMaxDate3.item,
qry_HersheySAPMaxDate3.MaxOfMaxOfjob_date, qry_HersheySAPMaxDate3.SumOfSumOfqty_on_hand, qry_HersheySAPMaxDate3.product_code,
qry_HersheySAPMaxDate3.whse,
qry_HersheySAPMaxDate3.jobnumber
FROM
qry_HersheySAPMaxDate2
LEFT JOIN qry_HersheySAPMaxDate3 ON (qry_HersheySAPMaxDate2.item = qry_HersheySAPMaxDate3.item) AND (qry_HersheySAPMaxDate2.ID = qry_HersheySAPMaxDate3.ID);
Result set using my query + the suggestion in one of the answers to use LEFT OUTER JOIN instead:
Screenshot
You complain about your query producing entirely blank rows. Let's see why:
You outer join qry3 to qry2. That means when there is no match for a qry2 record in qry3, then a pseudo qry3 record with all columns set to null gets joined.
In your query you select only fields from qry3, so in an outer join case they are all null. Select qry2.ID and qry2.item instead of qry3.ID and qry3.item to see the values that have no match:
SELECT
qry_HersheySAPMaxDate2.ID,
qry_HersheySAPMaxDate2.item,
You should use LEFT OUTER JOIN if you want the NULL values to be included in the result

Grouping in select query returns more rows than in actual selecting table?

I need to select few columns from table which contains 10 records, I need to group these 1 records by productId.
If I group all these records from product table which contains more than 120 records am getting more than 1000 records.
You most likely are not using the JOIN correctly. You need to say where the two (or more) tables have a value which is equal. Such as....
SELECT COUNT(a.AppleId) as AppleAmt, b.BasketLocation, c.ContainerColor
FROM Apples as a INNER JOIN
Baskets as b ON a.BasketId = b.BasketId INNER JOIN
Containers as c ON b.ContainerId = c.ContainerId
WHERE a.Rotten IS NULL and a.Eaten IS NULL and a.SoTasty IS NOT NULL
GROUP BY b.BasketLocation, c.ContainerColor
ORDER BY b.BasketLocation DESC