COUNT() return 0 on certain column - sql

Requirements:I wanted to know how many images that each person have posted.
Therefore I create a table schema as follow.
Table=Person
==========
Id (PK) , Column1, Column2, LId (FK)
Table=ListMaster
============
Id (PK) , LId (Unique)
Table = ListDetail
===========
Id (PK), LId(FK), DataId(FK)
Table = Image
=========
Id (PK), Column1, Column2
and use a query SQL
SELECT Person.Id AS PersonId,
Person.Column1 AS PersonC1,
Person.Column2 AS PersonC2,
COUNT(Image.Id) AS ImageCount
FROM Person
LEFT OUTER JOIN ListMaster ON Person.LId = ListMaster.LId
LEFT OUTER JOIN ListDetail ON ListDetail.LId = ListMaster.LId
LEFT OUTER JOIN Data AS Image ON ListDetail.DataId = Image.Id
GROUP BY Person.Id,
Person.Column1,
Person.Column2
I notice some person's "ImageCount" have "0" although there are images that he have posted before.
Could you please advise me how to fix my query or tell me it is even logically possible to do what I wanted? I suspect I make a mistake regarding my table design.
Sample Data
=======
Table = Person
Id (PK)(bigint identity) | Column1 (nvarchar(max)) | Column2 (nvarchar(max)) | LId (FK) (bigint [null])
1 | Test 1 C1 | Test1 C2 | 1
2 | Test 2 C1 | Test 2 C2 | 2
3 | Test 3 C1 | Test 3 C2 | NULL
4 | Test 4 C1 | Test 4 C4 | 37
Table = ListMaster
Id (PK)(bigint)(identity) | LId (Unique)(bigint)
1 | 1
2 | 2
3 | 37
Table = ListDetail
Id (PK)(bigint identity)| LId(FK)(bigint not null)| DataId(FK)(bigint not null)
1 | 1 | 1
2 | 1 | 2
3 | 2 | 3
4 | 37 | 4
Table = Image
Id (PK)(big int not null)(identity) | Column1 (nvarchar(max)) | Column2 (nvarchar(max))
1 | Location 1 | Dummy Data 1
2 | Location 2 | Dummy Data 2
3 | Location 3 | Dummy Data 3
4 | Location 4 | Dummy Data 4
I expect the COUNT(Image.Id) AS ImageCount should return
2
1
0
1
but it return
2
1
0
0
EDIT 1 : Change the table design
EDIT 2 : Add a sample data

If, as you say, your Image.Id column is declared NOT NULL, the only reason you should get a COUNT(Image.Id) of 0 for a Person would be if your LEFT JOINs do not find any Images for a given Person. In that case, Image.Id in your underlying results will be NULL, and therefore COUNT(Image.id) will be zero. This means that either:
There's a Person who doesn't have any ListMaster entries.
There's a ListMaster which doesn't have any ListDetail entries.
There's a ListDetail entry which doesn't have any Data entries.
...or some combination of the above.
You should be able to quickly check which links are missing by adding COUNTs for the appropriate tables to your existing query:
SELECT Person.Id AS PersonId,
Person.Column1 AS PersonC1,
Person.Column2 AS PersonC2,
-- NEXT TWO COUNTS ADDED FOR DEBUGGING
COUNT(ListMaster.LId) AS ListMasterCount,
COUNT(ListDetail.LId) AS ListDetailCount,
COUNT(Image.Id) AS ImageCount
FROM Person
LEFT OUTER JOIN ListMaster ON Person.LId = ListMaster.LId
LEFT OUTER JOIN ListDetail ON ListDetail.LId = ListMaster.LId
LEFT OUTER JOIN Data AS Image ON ListDetail.DataId = Image.Id
GROUP BY Person.Id,
Person.Column1,
Person.Column2

It looks like your query and data as given should return the values you expect. Is this the actual schema, data, and query, or are you simplifying to make this post? I presume your real data does not include values like "test 1 C1", etc. Did you create a DB with these dummy field names and values to do this test, or are you saying that this is the equivalent of what you really have? If this isn't the actual stuff, it could well be that in simplifying you have left out the thing that's really causing the problem.
When I have a query that does not give the expected results, I try to drop out parts of the query to see where the problem is. Like try with only the first join and see if you get the expected results. If that works, add in the second join, etc. Leave out the GROUP BY and just dump all the records, so you can see the actual records and not just the count.
There are many possible sources of trouble. Maybe the data isn't what you think it is. Maybe one of the joins is using the wrong field. Maybe you're getting in trouble because you have different data types and a conversion is not giving the results you expect. Etc.

Try this to check if all your Id, Column1, Column2 have image
SELECT Person.Id AS PersonId,
Person.Column1 AS PersonC1,
Person.Column2 AS PersonC2,
ListMaster.*,
ListDetail.*,
Data.*
FROM Person
LEFT OUTER JOIN ListMaster ON Person.LId = ListMaster.LId
LEFT OUTER JOIN ListDetail ON ListDetail.LId = ListMaster.LId
LEFT OUTER JOIN Data AS Image ON ListDetail.DataId = Image.Id
ORDER BY Person.Id,
Person.Column1,
Person.Column2

Related

Complex SQL Joins with Where Clauses

Being pretty new to SQL, I ask for your patience. I have been banging my head trying to figure out how to create this VIEW by joining 3 tables. I am going to use mock tables, etc to keep this very simple. So that I can try to understand the answer - no just copy and paste.
ICS_Supplies:
Supplies_ ID Item_Description
-------------------------------------
1 | PaperClips
2 | Rubber Bands
3 | Stamps
4 | Staples
ICS_Orders:
ID SuppliesID RequisitionNumber
----------------------------------------------------
1 | 1 | R1234a
6 | 4 | R1234a
2 | 1 | P2345b
3 | 2 | P3456c
4 | 3 | R4567d
5 | 4 | P5678e
ICS_Transactions:
ID RequsitionNumber OrigDate TransType OpenClosed
------------------------------------------------------------------
1 | R1234a | 06/12/20 | Req | Open
2 | P2345b | 07/09/20 | PO | Open
3 | P3456c | 07/14/20 | PO | Closed
4 | R4567d | 08/22/20 | Req | Open
5 | P5678e | 11/11/20 | PO | Open
And this is what I want to see in my View Results
Supplies_ID Item RequsitionNumber OriginalDate TransType OpenClosed
---------------------------------------------------------------------------------------
1 | Paper Clips | P2345b | 07/09/20 | PO | OPEN
2 | Rubber Bands | Null | Null | Null | Null
3 | Stamps | Null | Null | Null | Null
4 | Staples | P56783 | 11/11/20 | PO | OPEN
I just can't get there. I want to always have the same amount of records that we have in the ICS_Supplies Table. I need to join to the ICS_Orders Table in order to grab the Requisition Number because that's what I need to join on the ICS_Transactions Table. I don't want to see data in the new added fields UNLESS ICS_Transactions.TransType = 'PO' AND ICS_Transactions.OpenClosed = 'OPEN', otherwise the joined fields should be seen as null, regardless to what they contain. IF that is possible?
My research shows this is probably a LEFT Join, which is very new to me. I had made many attempts on my own, and then posted my question yesterday. But I was struggling to ask the correct question and it was recommended by other members that I post the question again . .
If needed, I can share what I have done, but I fear it will make things overly confusing as I was going in the wrong direction.
I am adding a link to the original question, for those that need some background info
Original Question
If there is any additional information needed, just ask. I do apologize in advance if I have left out any needed details.
This is a bit tricky, because you want to exclude rows in the second table depending on whether there is a match in the third table - so two left joins are not what you are after.
I think this implements the logic you want:
select s.supplies_id, s.item_description,
t.requisition_number, t.original_date, t.trans_type, t.open_closed
from ics_supplies s
left join ics_transaction t
on t.transtype = 'PO'
and t.open_closed = 'Open'
and exists (
select 1
from ics_order o
where o.supplies_id = s.supplies_id and o.requisition_number = t.requisition_number
)
Another way to phrase this would be an inner join in a subquery, then a left join:
select s.supplies_id, s.item_description,
t.requisition_number, t.original_date, t.trans_type, t.open_closed
from ics_supplies s
left join (
select o.supplies_id, t.*
from ics_order o
inner join ics_transaction t
on t.requisition_number = o.requisition_number
where t.transtype = 'PO' and t.open_closed = 'Open'
) t on t.supplies_id = s.supplies_id
This query should return the data for supplies. The left join will add in all orders that have a supply_id (and return null for the orders that don't).
select
s.supplies_id
,s.Item_Description as [Item]
,t.RequisitionNumber
,t.OrigDate as [OriginalDate]
,t.TransType
,t.OpenClosed
from ICS_Supplies s
left join ICS_Orders o on o.supplies_id = s.supplies_id
left join ICS_Transactions t on t.RequisitionNumber = o.RequisitionNumber
where t.TransType = 'PO'
and t.OpenClosed = 'Open'
The null values will automatically show null if the record doesn't exist. For example, you are joining to the Transactions table and if there isn't a transaction_id for that supply then it will return 'null'.
Modify your query, run it, then maybe update your question using real examples if it's possible.
In the original question you wrote:
"I only need ONE matching record from the ICS_Transactions Table.
Ideally, the one that I want is the most current
'ICS_Transactions.OriginalDate'."
So the goal is to get the most recent transaction for which the TransType is 'PO' and OpenClosed is 'Open'. That the purpose of the CTE 'oa_cte' in this code. The appropriate transactions are then LEFT JOIN'ed on SuppliesId. Something like this
with oa_cte(SuppliesId, RequsitionNumber, OriginalDate,
TransType, OpenClosed, RowNum) as (
select o.SuppliesId, o.RequsitionNumber,
t.OrigDate, t.TransType, t.OpenClosed,
row_number() over (partition by o.SuppliesId
order by t.OrigDate desc)
from ICS_Orders o
join ICS_Transactions t on o.RequisitionNumber=t.RequisitionNumber
where t.TransType='PO'
and t.OpenClosed='OPEN')
select s.*, oa.*
from ICS_Supplies s
left join oa_cte oa on s.SuppliesId=oa.SuppliesId
and oa.RowNum=1;

how to get a record based on max date for each user in different multiple tables

I'm trying to make a query that can select the names, DateOfSpecimenResult,SwabResult,DateOfReleaseOfResult. Heres the Visual Representation:
Table: ContactTracingHeader
| AutoID | Name |
1 Jason
2 Chris
Table: Swab
| PatientNo | DateOfSpecimenCollection | SwabResultID | DateOfReleaseOfResult |
1 05/02/2020 1 05/10/2020
1 06/08/2020 1 06/11/2020
1 07/16/2020 2 07/20/2020
Note: ContactTracingHeader.AutoID = Swab.PatientNo
Table: SwabResult
| AutoID | SwabResult |
1 POSITIVE
2 NEGATIVE
Query Output that I'm trying to make
| AutoID | Name | DateOfSpecimenCollection | SwabResult | DateOfReleaseOfResult |
1 Jason 07/16/2020 NEGATIVE 07/20/2020
2 Chris (BLANK) (BLANK) (BLANK)
Note: Swab.ResultID = SwabResult.AutoID
Here, I'm only trying to show a Name with the latest DateOfSpecimenCollection and then use it as a reference for the 2 other column which is SwabResult and DateOfReleaseOfResult, And Since "Chris" doesn't have a input on the Swab table, his records are blank but his name and AutoID Still appears on the table. The SwabResult shows the NEGATIVE, or POSITIVE depending on the ID from table Swab.
What i have done so far is this:
SELECT
CTH.AutoID,
CTH.Firstname,
CTH.Lastname,
(SELECT MAX(DateOfSpecimenCollection)
FROM Swab
WHERE Swab.PatientNo = CTH.AutoID
) AS DateOfSpecimenCollection,
Swab.SwabResultID,
Swab.DateOfReleaseOfResult
FROM ContactTracingHeader AS CTH LEFT JOIN Swab ON Swab.PatientNo = CTH.AutoID;
This Query gets the latest DateOfSpecimenCollection and Output those who aren't in the Swab table which is correct, however it duplicates the record depending how many SwabResult or DateOfReleaseOfResult it has. Also I tried INNER JOIN the table SwabResult so i can output the SwabResult instead of the ID, However it gave me JOIN Expression not supported Error.
I apologize for the long and confusing explanation and duplicate question as I'm trying to strugge to find some answers on the internet. Thank you!
Need a unique identifier in Swab table. If not already there, autonumber should serve
Consider:
Query1:
SELECT Swab.* FROM Swab WHERE ID IN
(SELECT TOP 1 ID FROM Swab AS Dupe WHERE Dupe.PatientNo=Swab.PatientNo
ORDER BY Dupe.DateOfSpecimenCollection DESC);
Query2
SELECT ContactTracingHeader.AutoID, ContactTracingHeader.Name, Query1.DateOfSpecimenCollection,
SwabResult.SwabResult, Query1.DateOfReleaseOfResult
FROM ContactTracingHeader LEFT JOIN (SwabResult RIGHT JOIN Query1
ON SwabResult.AutoID = Query1.SwabResultID)
ON ContactTracingHeader.AutoID = Query1.PatientNo;
Or this sequence:
Query1:
SELECT Swab.PatientNo, Max(Swab.DateOfSpecimenCollection) AS MaxOfDateOfSpecimenCollection
FROM Swab
GROUP BY Swab.PatientNo;
Query2:
SELECT Swab.PatientNo, Swab.DateOfSpecimenCollection, Swab.SwabResultID,
Swab.DateOfReleaseOfResult
FROM Query1 INNER JOIN Swab
ON (Query1.MaxOfDateOfSpecimenCollection = Swab.DateOfSpecimenCollection)
AND (Query1.PatientNo = Swab.PatientNo);
Query3:
SELECT ContactTracingHeader.AutoID, ContactTracingHeader.Name,
Query2.DateOfSpecimenCollection, SwabResult.SwabResult, Query2.DateOfReleaseOfResult
FROM SwabResult RIGHT JOIN (ContactTracingHeader LEFT JOIN Query2
ON ContactTracingHeader.AutoID = Query2.PatientNo)
ON SwabResult.AutoID = Query2.SwabResultID;

INNER JOIN Need to use column value twice in results

I've put in the requisite 2+ hours of digging and not getting an answer.
I'd like to merge 3 SQL tables, where Table A and B share a column in common, and Table B and C share a column in common--Tables A and C do not.
For example:
Table A - entity_list
entity_id | entity_name | Other, irrelevant columns
Example:
1 | Microsoft |
2 | Google |
Table B - transaction_history
transaction_id | purchasing_entity | supplying_entity | other, irrelevant columns
Example:
1 | 2 | 1
Table C - transaction_details
transactional_id | amount_of_purchase | Other, irrelevant columns
1 | 5000000 |
Using INNER JOIN, I've been able to get a result where I can link entity_name to either purchasing_entity or supplying_entity. And then, in the results, rather than seeing the entity_id, I get the entity name. But I want to substitute the entity name for both purchasing and supplying entity.
My ideal results would look like this:
1 [transaction ID] | Microsoft | Google | 5000000
The closes I've come is:
1 [transaction ID] | Microsoft | 2 [Supplying Entity] | 5000000
To get there, I've done:
SELECT transaction_history.transaction_id,
entity_list.entity_name,
transaction_history.supplying_entity,
transaction_details.amount_of_purchase
FROM transaction.history
INNER JOIN entity_list
ON transaction_history.purchasing_entity=entity_list.entity.id
INNER JOIN
ON transaction_history.transaction_id=transaction_details.transaction_id
I can't get entity_name to feed to both purchasing_entity and supplying_entity.
Here is the query:
SELECT h.transaction_id, h.purchasing_entity, purchaser.entity_name, h.supplying_entity, supplier.entity_name, d.amount_of_purchase
FROM transaction_history h
INNER JOIN transaction_details d
ON h.transaction_id = d.transaction_id
INNER JOIN entity_list purchaser
ON h.purchasing_entity = purchaser.entity_id
INNER JOIN entity_list supplier
ON h.supplying_entity = supplier.entity_id

SQL many-to-many select help needed

I have 2 tables
Bid_customer
|bidkey | customerkey
| 1 | 1
| 1 | 2
| 1 | 3
customer_groups
| groupkey | customerkey
| 1 | 1
| 1 | 2
| 1 | 3
What I'm trying to get is a result that will look like
| bidkey | groupkey
| 1 | 1
I've tried a cursor and joins but just don't seem to be able to get what i need any ideas or suggestions
EDIT: customers can belong to more that one group also
I am not sure who meaningful your sample data is. However following is a simple example.
Query:
select distinct b.bidkey, g.gkey
from bidcus b
inner join cusgroup g
on
b.cuskey = g.cuskey
and g.gkey = 10;
Results:
BIDKEY GKEY
1 10
Reference: SQLFIDDLE
In order to have a working Many-to-Many relationship in a database you need to have an intermediary table that defines the relationship so you do not get duplicates or mismatched values.
This select statement will join all bids with all groups because the customer matches.
Select bidkey, groupkey
From customer_groups
Inner Join bid_customer
Where customer_groups.customerkey = Bid_customer.customerkey
Hers is a sample Many to Many Relationship:
For your question:
You will need another table that joins the data. For example, GroupBids
customer_groups and bid_customer would have a one-to-many relationship with GroupBids
You would then do the following select to get your data.
Select bidkey, groupkey
From bid_customer
inner join GroupBids
ON bid_customer.primarykey = GroupBids.idBidKey
inner join customer_groups
ON customer_groups.primarykey = GroupBids.idCustomerGroupkey
This would make sure only related groups and bids are returned

My SQL query within a query

I have 2 tables that I am trying to combine in a specific way
Table 1: ‘part_defs’ Table 2 Items_part_values
in value_defs:
ID | Name
-------------
1 | color
2 | size
3 | weight
in Items_part_values
ItemID | valueID | Value
-------------------------
10 | 1 | red
11 | 1 | blue
What I need is a query where for a given item all the rows from value_defs appear and if they have a value in Items_part_values the value.
So for Item 11 I want
ID | Name | Value
--------------------
1 | color | red
2 | size | NULL
3 | weight | NULL
I’m new to MySQL, in access I would have created a subquery with the ItemID as a parameter and then done a Left Join with value_defs on the result.
Is there a way of doing something similar in MySQL?
Thanks
Use:
SELECT p.id,
p.name,
ipv.value
FROM PART_DEFS p
LEFT JOIN ITEMS_PART_VALUES ipv ON ipv.valueid = p.id
AND ipv.itemid = ?
Replace the "?" with the itemid you want to search for.
This means all the PARTS_DEF rows will be returned, and if the ITEMS_PART_VALUES.valueid matches the PART_DEFS.id value, then the ITEMS_PART_VALUES.value value will be displayed for the item you are looking for. If there's no match, the value column will be NULL for that record.
There's a difference in OUTER JOINs, when specifying criteria in the JOIN vs the WHERE clause. In the JOIN, the criteria is applied before the JOIN occurs while in the WHERE clause the criteria is applied after the JOIN.
Use a left join:
SELECT * FROM Table1 LEFT JOIN Table2 USING (ID);
Edit:
SELECT * FROM part_defs LEFT JOIN Items_part_values ON part_defs.ID = Items_part_values.valueID;