Complex SQL Joins with Where Clauses - sql

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;

Related

SQL Server avoid repeat same joins

I´m doing the query below where I´m repeating the same joins multiple times, there is a better way to do it? (SQL Server Azure)
Ex.
Table: [Customer]
[Id_Customer] | [CustomerName]
1 | Tomy
...
Table: [Store]
[Id_Store] | [StoreName]
1 | SuperMarket
2 | BestPrice
...
Table: [SalesFrutes]
[Id_SalesFrutes] | [FruteName] | [Fk_Id_Customer] | [Fk_Id_Store]
1 | Orange | 1 | 1
...
Table: [SalesVegetable]
[Id_SalesVegetable] | [VegetableName] | [Fk_Id_Customer] | [Fk_Id_Store]
1 | Pea | 1 | 2
...
Select * From [Customer] as C
left join [SalesFrutes] as SF on SF.[Fk_Id_Customer] = C.[Id_Customer]
left join [SalesVegetable] as SV on SV.[Fk_Id_Customer] = C.[Id_Customer]
left join [Store] as S1 on S1.[Id_Store] = SF.[Fk_Id_Store]
left join [Store] as S2 on S1.[Id_Store] = SV.[Fk_Id_Store]
In my real case, I have many [Sales...] to Join with [Customer] and many other tables similar to [Store] to join to each [Sales...]. So it starts to scale a lot the number on joins repeating. There is a better way to do it?
Bonus question: I do like also to have FruteName, VegetableName, StoreName, and each Food table name under the same column.
The Expected Result is:
[CustomerName] | [FoodName] | [SalesTableName] | [StoreName]
Tomy | Orange | SalesFrute | SuperMarket
Tomy | Pea | SalesVegetable | BestPrice
...
Thank you!!
So based on the information provided, I would have suggested the below, to use a cte to "fix" the data model and make writing your query easier.
Since you say your real-world scenario is different to the info provided it might not work for you, but could still be applicable if you have say 80% shared columns, you can just use placeholder/null values where relevant for unioning the data sets and still minimise the number of joins eg to your store table.
with allSales as (
select Id_SalesFrutes as Id, FruitName as FoodName, 'Fruit' as SaleType, Fk_Id_customer as Id_customer, Fk_Id_Store as Id_Store
from SalesFruits
union all
select Id_SalesVegetable, VegetableName, 'Vegetable', Fk_Id_customer, Fk_Id_Store
from SalesVegetable
union all... etc
)
select c.CustomerName, s.FoodName, s.SaleType, st.StoreName
from Customer c
join allSales s on s.Id_customer=c.Id_customer
join Store st on st.Id_Store=s.Id_Store

combine 2 sql's from different tables into one query

I have for example as first query: (ararnr = article number)
Select ararnr,ararir,aoarom from ar left join ao ON AR.ARARNR=AO.AOARNR WHERE AR.ARARKD=1389
the second query uses the result from the first column from the first query to search in another table
Select votgan, sum(ststan) as totalStock from vo INNER JOIN st on vo.voarnr=st.starnr where voarnr = ararnr
How could I combine both ?
Please note : Not all articlenumbers from the first query will be found in the second, but I need them in my result.
In the result I need the columns from both queries.
EDIT
for example :
first query returns article numbers and the description:
+---------+--------------+
| ararnr | aoarom |
+---------+--------------+
| a123456 | description1 |
| b123456 | description2 |
| 0123456 | description3 |
+---------+--------------+
second query returns the totalstock for those articles:
+---------+--------------+
| ararnr | totalstock |
+---------+--------------+
| a123456 | 12 |
| b123456 | |
| 0123456 | 6 |
+---------+--------------+
Note the second one doesn't return a value since the articlenumber doesn't exist in this table.
In my result I would like to get the articlenumber with corresponding description and stock.
+---------+--------------+-----------+---------+
| ararnr | aoarom | totalStock| vovoan |
+---------+--------------+-----------+---------+
| a123456 | description1 | 12 | 2 |
| b123456 | description2 | | 1 |
| 0123456 | description3 | 6 | |
+---------+--------------+-----------+---------+
I'm using sql on db2
SECOND EDIT
The first query will select some article numbers (ararnr) from table ar and find the corresponding description (aoarom) in another table ao.
The second query finds the stock (vovoan and sum ststan) from two differend tables vo and st for the article numbers found in the first query.
The result should have the article number with corresponding description with corresponding stock from vo and st
I can't fully understand what you're asking, but another join may assist you.
example:
SELECT ar.ararnr, ar.ararir, ar.ararom, vo.votgan, SUM(vo.ststan) as totalStock
FROM ar LEFT JOIN ao ON [id=id] LEFT JOIN vo ON [id=id]
Because I can't tell what your tables structure are, or what you're really asking for, this is the best response I can give you.
This also may be what you're looking for:
Combining 2 SQL queries and getting result set in one
You can use this query.
SELECT ar.ararnr, ar.ararir, ar.ararom, vo.votgan, SUM(vo.ststan) as totalStock
FROM ar
LEFT JOIN ao ON ao.ararnr = ar.ararnr
LEFT JOIN vo ON vo.voarnr = ao.ararnr
If you are using SQL Server as database then this can be done with help of OUTER APPLY
SELECT ararnr,aoarom ,temp.totalStock
FROM ar
LEFT JOIN ao ON AR.ARARNR=AO.AOARNR
OUTER APPLY(
SELECT sum(ststan) as totalStock
FROM vo
INNER JOIN st on vo.voarnr=st.starnr
where voarnr = ar.ararnr
)temp
WHERE AR.ARARKD=1389
You'd get a much more complete answer if you were to post the table structure and desired result, but..
You can use the first query as a resultset for your second query, and join to it. something like:
Select
votgan,
sum(ststan) as totalStock
from vo
inner join (Select
ararnr,
ararir,
ararom
from ar
left join ao .....) z on vo.voarnr = z.ararnr
EDIT:
Select
votgan,
sum(ststan) as totalStock,
z.ararnr,
z.aoarom
from vo
inner join (Select
ararnr,
ararir,
ararom
from ar
left join ao .....) z on vo.voarnr = z.ararnr

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

Access SQL Max-Function

I have a question concerning MS Access queries involving these tables:
tblMIDProcessMain ={ Process_ID,Process_Title,...}
tblMIDProcessVersion = { ProcessVersion_ID, ProcessVersion_FK_Process, ProcessVersion_VersionNo, ProcessVersion_FK_Status, ...}
tblMIDProcessVersionStatus = { ProcessVersionStatus_ID,ProcessVersionStatus_Value }
The tables store different versions of a process description. The "ProcessVersion_VersionNo" field contains an integer providing the version number. Now I would like to get for each process the highest version number thus the current version. If I do the following it kind of works:
SELECT tblMIDProcessMain.Process_Titel
, Max(tblMIDProcessVersion.ProcessVersion_VersionNo) AS CurrentVersion
FROM tblMIDProcessMain
INNER JOIN tblMIDProcessVersion
ON tblMIDProcessMain.Process_ID = tblMIDProcessVersion.ProcessVersion_FK_Process
GROUP BY tblMIDProcessMain.Process_Titel;
The query returns a recordset with each existing process_title and the respective max number of the version field. But as soon as I add other fields like "ProcessVersion_FK_Status" in the Select statement the query stops working.
Any help would be appreciated. Thanks in advance.
Jon
Edit:
To clarify things a little I added a simplified example
Parent-Table:
Process_ID | Process_Title
----------------------------------
1 | "MyProcess"
2 | "YourProcess"
Child-Table:
Version_ID | Version_FK_ProcessID | Version_No | Version_Status
---------------------------------------------------------------------------
1 | 1 | 1 | "New"
2 | 2 | 1 | "Discarded"
3 | 2 | 2 | "Reviewed"
4 | 2 | 3 | "Released"
Intended Result:
Title | Max_Version_No | Status
--------------------------------------------------------
MyProcess | 1 | "New"
YourProcess | 3 | "Released"
Given the example tables you updated your post with, this should work:
select process_title as Title
, max_version.max_version_no
, c.version_status as status
from (parenttable p
inner join (select max(version_id) as max_version_no, version_fk_process_id from childtable group by version_fk_process_id) max_version
on p.process_id = max_version.version_fk_process_id)
inner join childtable c
on max_version.max_version_no = c.version_id and max_version.version_fk_process_id = c.version_fk_process_id
I assume you are adding the new field to the 'Group By" clause? If not, then you either must include in the 'Group By', or you must use one of the operators like "Max" or "First" etc.

SQL Order By Within A Count(Distinct)

I have the following tables:
filetype1
F1_ID | F1_ORDR | FILENAME
1 | 1 | file1.txt
2 | 2 | file2.txt
3 | 3 | file3.txt
4 | 2 | file4.txt
5 | 4 | file5.txt
filetype2
F2_ID | F2_ORDR | FILENAME
1 | 1 | file6.txt
2 | 2 | file7.txt
3 | 4 | file8.txt
ordr
OR_ID | OR_VENDOR
1 | 1
2 | 1
3 | 1
4 | 1
vendor
VE_ID | VE_NAME
1 | Company1
My goal is to have a list of vendors and a count of the number of orders where a file is connected for each type. For example, the end result of this data should be:
VENDOR | OR_CT | F1_CT | F2_CT
Company1 | 4 | 4 | 3
Because at least 1 type1 file was attached to 4 distinct orders and at least 1 type2 file was attached to 3 distinct orders. Currently my SQL code looks like this:
SELECT vendor.ve_id, vendor.ve_name,
(SELECT COUNT(or_id)
FROM ordr
WHERE ordr.or_vendor = vendor.ve_id) as OR_COUNT,
(SELECT COUNT(DISTINCT f1_order)
FROM filetype1 INNER JOIN ordr ON filetype1.f1_ordr = ordr.or_id
WHERE ordr.or_vendor = vendor.ve_id) as F1_CT,
(SELECT COUNT(DISTINCT f2_ordr)
FROM filetype2 INNER JOIN ordr ON filetype2.f2_ordr = ordr.or_id
WHERE ordr.or_vendor = vendor.ve_id) as F2_CT
FROM vendor
ORDER BY vendor.ve_name
Unfortunately this yields the following results:
VENDOR | OR_COUNT | F1_COUNT | F2_COUNT
Company1 | 4 | 5 | 3
My only guess is that because I'm using COUNT(DISTINCT) the COUNT is automatically assuming the DISTINCT is ordering by F1_ID instead of by F1_ORDR
If anyone can assist me on how to tell the COUNT(DISTINCT) to order by F1_ORDR that would be most helpful. I have searched the vast internet for a solution but its hard to explain what I want to a search engine, forums, etc. My database uses Microsoft SQL Server. My knowledge of database management is almost completely self taught, so I'm just glad I made it this far on my own. My expertise is in web design. Thank you for your time.
Your SQL yields the result you want for me.
Two pieces of advice
Order is a bad name for a table - it conflicts with a reserved word, and will cause you no end of hassle
You should join your tables like so
FROM filetype1
inner join [order]
on filetype1.f1_order = or_id
rather than using a where clause
Perhaps try this instead
select
vendor.VE_ID, vendor.VE_NAME,
count(distinct or_id),
count(distinct f1_order),
count(distinct f2_order)
from
vendor
left join [order]
on vendor.VE_ID = [order].OR_VENDOR
inner join filetype1
on [order].OR_ID = filetype1.F1_ORDER
left join filetype2
on [order].OR_ID = filetype2.F2_ORDER
group by
vendor.VE_ID, vendor.VE_NAME
Try this:
SELECT
vdr.VE_NAME
,COUNT(DISTINCT OR_ID) AS OR_ID
,COUNT(DISTINCT ft1.F1_ORDER) AS FT1_COUNT
,COUNT(DISTINCT ft2.F2_ORDER) AS FT2_COUNT
FROM
vendor vdr
LEFT OUTER JOIN [order] odr
ON vdr.VE_ID = odr.OR_VENDOR
INNER JOIN filetype1 ft1
ON odr.OR_ID = ft1.F1_ORDER
LEFT OUTER JOIN filetype2 ft2
ON odr.OR_ID = ft2.F2_ORDER
GROUP BY
vdr.VE_ID
,vdr.VE_NAME
I will propose you this:
Merge filetype1 and filetype2 tables in one table(filetype) and add another field named - f_type(for instance) of type INT or TINTYINT to store the filetype (1 or 2). This has the benefits of painlessly adding another filetype later
Now the query will look something like this:
SELECT
vendor.ve_name,
count(DISTINCT filetype.f_order),
filetype.f_type
FROM
filetype
INNER JOIN `order`
ON filetype.f_order = `order`.or_id
INNER JOIN vendor
ON `order`.or_vendor = vendor.ve_id
GROUP BY vendor.ve_id,filetype.f_type
This will give the count of orders for filetype.
For the total orders just add another query:
SELECT count(*) FROM `order`