Table A drops rows in table B in SQL JOIN - sql

I have two tables which I need joined. I have two select statements with the same columns except OrderValue and SalesTarget:
SELECT
b.TrnYear, b.TrnMonth, b.Branch, b.Salesperson, b.OrderValue
FROM
dbo.vw_jab_Consolidated_Orders as b
INNER JOIN
dbo.vw_jab_SalTargets as a ON a.Sequence2 = b.Salesperson
WHERE
b.TrnYear = '2017'
ORDER BY
a.TrnMonth
SELECT
a.TrnYear, a.TrnMonth, a.Sequence1, a.Sequence2, a.SalesTarget
FROM
dbo.vw_jab_SalTargets as a
WHERE
a.TrnYear = '2017' AND a.SequenceType = 'BR'
GROUP BY
a.TrnYear, a.TrnMonth, a.Sequence1, a.Sequence2, a.SalesTarget
My problem is that as there are months where there are no OrderValues, the SalesTarget value is being filtered out when I join the two tables as follows:
SELECT
a.TrnYear, SUM(a.SalesTarget) as SalesTarget, SUM(b.InvoicedSales) AS InvoicedSales
FROM
(SELECT
TrnYear, SUM(SalesTarget) AS SalesTarget
FROM
dbo.vw_jab_SalTargets
WHERE
(SequenceType = 'BR')
GROUP BY
SequenceType, TrnYear) AS A
LEFT JOIN
(SELECT
TrnYear, SUM(ActualSales) AS InvoicedSales
FROM
dbo.vw_jab_Consolidated_Sales
GROUP BY
TrnYear) AS b ON a.TrnYear = b.TrnYear
WHERE
a.TrnYear = '2017'
GROUP BY
a.TrnYear

You want a right outer join, instead of an inner join. For a right outer join, all columns of the right table are kept even if there is not a matching value in the left table.
Try this:
SELECT a.TrnYear, a.TrnMonth, SUM(a.SalesTarget) as SalesTarget, SUM(b.InvoicedSales) AS InvoicedSales
FROM (SELECT TrnYear, TrnMonth, SUM(SalesTarget) AS SalesTarget
FROM dbo.vw_jab_SalTargets
WHERE (SequenceType = 'BR')
GROUP BY SequenceType, TrnYear, TrnMonth) AS a RIGHT OUTER JOIN
(SELECT TrnYear, TrnMonth, SUM(ActualSales) AS InvoicedSales
FROM dbo.vw_jab_Consolidated_Sales
GROUP BY TrnYear, TrnMonth) AS b ON a.TrnYear = b.TrnYear and a.TrnMonth=b.TrnMonth
WHERE a.TrnYear = '2017'
GROUP BY a.TrnYear, a.TrnMonth
ORDER BY TrnMonth

You could switch to a RIGHT JOIN, but then if you have no InvoicedSales, you won't get any SalesTargets. I'd use a FULL OUTER JOIN like so:
SELECT COALESCE(a.TrnYear,b.TrnYear) AS TrnYear
,SUM(a.SalesTarget) AS SalesTarget
,SUM(b.InvoicedSales) AS InvoicedSales
FROM (
SELECT TrnYear
,SUM(SalesTarget) AS SalesTarget
FROM dbo.vw_jab_SalTargets
WHERE (SequenceType = 'BR')
GROUP BY SequenceType
,TrnYear
) AS a
FULL OUTER JOIN (
SELECT TrnYear
,SUM(ActualSales) AS InvoicedSales
FROM dbo.vw_jab_Consolidated_Sales
GROUP BY TrnYear
) AS b
ON a.TrnYear = b.TrnYear
WHERE a.TrnYear = '2017'
OR b.TrnYear = '2017'
GROUP BY COALESCE(a.TrnYear,b.TrnYear)
The COALESCE() is necessary so that the column will always return the TrnYear.

Related

Get the count of records from another table

I have a view that looks like this:
which is defined using this SQL statement:
SELECT
p.p_ccode AS C_CODE,
p.p_pcode AS P_CODE,
p.p_phcode AS PH_CODE,
PO.m_doc_type AS TYPE,
PO.crtime AS DATE
FROM
TABLE1 AS p
LEFT OUTER JOIN
DOCTABLE AS PO ON PO.m_ccode = p.p_ccode
AND PO.m_pcode = p.p_pcode
AND PO.m_phcode = p.p_phcode
Now I want to count the records for another table (TABLE 2) based on the C_CODE, P_CODE and PH_CODE and display it as a column TOTAL in my view.
How can I do this?
one way is to use subquery:
SELECT
p.p_ccode AS C_CODE,
p.p_pcode AS P_CODE,
p.p_phcode AS PH_CODE,
PO.m_doc_type AS TYPE,
PO.crtime AS DATE,
(select count(*) from tale2 t2 where t2.C_CODE = p.p_ccode and t2.P_CODE = p.P_CODE and t2.PH_CODE = p.PH_CODE) as total
FROM TABLE1 AS p
LEFT OUTER JOIN DOCTABLE AS PO
ON PO.m_ccode = p.p_ccode
AND PO.m_pcode = p.p_pcode
AND PO.m_phcode = p.p_phcode
Left join to it, group by and then count --
SELECT
p.p_ccode AS C_CODE,
p.p_pcode AS P_CODE,
p.p_phcode AS PH_CODE,
PO.m_doc_type AS TYPE,
PO.crtime AS DATE,
COUNT(P2.*) AS TOTAL
FROM TABLE1 AS p
LEFT OUTER JOIN DOCTABLE AS PO
ON PO.m_ccode = p.p_ccode
AND PO.m_pcode = p.p_pcode
AND PO.m_phcode = p.p_phcode
LEFT OUTER JOIN TABLE2 AS p2
ON p.m_ccode = p2.p_ccode
AND p.m_pcode = p2.p_pcode
AND p.m_phcode = p2.p_phcode
GROUP BY p.p_ccode, p.p_pcode, p.p_phcode, PO.m_doc_type, PO.crtime
use an CTE (Common Table Expression)
WITH CTE_COUNT
AS(
SELECT
[C_CODE]
,[P_CODE]
,[PH_CODE]
,COUNT(*) AS [TOTAL]
FROM [dbo].[TABLE2]
GROUP BY
[C_CODE]
,[P_CODE]
,[PH_CODE]
)
SELECT
t1.[C_CODE]
,t1.[P_CODE]
,t1.[PH_CODE]
,t1.[TYPE]
,t1.[DATE]
,t2.[TOTAL]
FROM [dbo].[TABLE1] AS t1
LEFT JOIN CTE_COUNT AS t2
ON t2.[C_CODE] = t1.[C_CODE]
AND t2.[P_CODE] = t1.[P_CODE]
AND t2.[PH_CODE] = t1.[PH_CODE]

Group BY Expression column

we're trying to make our table add together all values in column 2 (QtyComp - an expression column of qtyorder * totalqty basically), where they have the same ItemNo (column 1).
So, we currently get the below:
ItemNo QtyCom
7441 3
7441 1
7441 5
What we want is it to return this:
ItemNo QtyCom
7441 9
Our code is below; I've bolded the part that we need it to sum the results of:
SELECT TOP (100) PERCENT ItemSpecs_2.itemno,
workorderdetails.qtycomplete *
ItemSpecFullStruc_2.totalqtyperroot AS QtyComp
FROM dbo.workorderdetails AS WorkOrderDetails
INNER JOIN dbo.itemspecfullstruc AS ItemSpecFullStruc_2
ON ItemSpecFullStruc_2.rootitemspecid =
workorderdetails.itemspecid
INNER JOIN dbo.itemspecs AS ItemSpecs_2
ON ItemSpecs_2.itemspecid = ItemSpecFullStruc_2.childitemspecid
INNER JOIN dbo.workorder AS WorkOrder_1
ON WorkOrder_1.workorderid = workorderdetails.workorderid
LEFT OUTER JOIN dbo.tobescheduled_completed
ON WorkOrder_1.workorderid =
dbo.tobescheduled_completed.workorderid
WHERE ( workorderdetails.completed = 1 )
AND ( workorderdetails.compdate > Getdate() - 42 )
GROUP BY ItemSpecs_2.itemno,
workorderdetails.qtyordered,
ItemSpecFullStruc_2.totalqtyperroot,
workorderdetails.[lineno],
workorderdetails.qtycomplete,
workorderdetails.compdate,
workorderdetails.qtycomplete * ItemSpecFullStruc_2.totalqtyperroot
We would really appreciate some ideas!
Thanks,
Trish
Try this
SELECT TOP (100) PERCENT ItemSpecs_2.itemno,
sum(workorderdetails.qtycomplete *
ItemSpecFullStruc_2.totalqtyperroot) AS QtyComp
FROM dbo.workorderdetails AS WorkOrderDetails
INNER JOIN dbo.itemspecfullstruc AS ItemSpecFullStruc_2
ON ItemSpecFullStruc_2.rootitemspecid =
workorderdetails.itemspecid
INNER JOIN dbo.itemspecs AS ItemSpecs_2
ON ItemSpecs_2.itemspecid = ItemSpecFullStruc_2.childitemspecid
INNER JOIN dbo.workorder AS WorkOrder_1
ON WorkOrder_1.workorderid = workorderdetails.workorderid
LEFT OUTER JOIN dbo.tobescheduled_completed
ON WorkOrder_1.workorderid =
dbo.tobescheduled_completed.workorderid
WHERE ( workorderdetails.completed = 1 )
AND ( workorderdetails.compdate > Getdate() - 42 )
GROUP BY ItemSpecs_2.itemno,
workorderdetails.qtyordered,
ItemSpecFullStruc_2.totalqtyperroot,
workorderdetails.[lineno],
workorderdetails.qtycomplete,
workorderdetails.compdate
Once you will use top for select statement, you need to use order by. you can try the following query.
SELECT TOP(100) PERCENT A.itemno,SUM(QtyComp) FROM
(SELECT ItemSpecs_2.itemno,
(workorderdetails.qtycomplete *
ItemSpecFullStruc_2.totalqtyperroot) AS QtyComp
FROM dbo.workorderdetails AS WorkOrderDetails
INNER JOIN dbo.itemspecfullstruc AS ItemSpecFullStruc_2
ON ItemSpecFullStruc_2.rootitemspecid =
workorderdetails.itemspecid
INNER JOIN dbo.itemspecs AS ItemSpecs_2
ON ItemSpecs_2.itemspecid = ItemSpecFullStruc_2.childitemspecid
INNER JOIN dbo.workorder AS WorkOrder_1
ON WorkOrder_1.workorderid = workorderdetails.workorderid
LEFT OUTER JOIN dbo.tobescheduled_completed
ON WorkOrder_1.workorderid =
dbo.tobescheduled_completed.workorderid
WHERE ( workorderdetails.completed = 1 )
AND ( workorderdetails.compdate > Getdate() - 42 ) ) A
GROUP BY A.itemno
ORDER BY A.itemno
Thanks
SELECT SUM(QTYCOM) OVER (PARTITION BY ITEMNO)
FROM
...

Easy Left Join SQL Syntax

New to SQL and want to complete a LEFT JOIN.
I have two seperate tables with the below code:
SELECT
StockCode, SalesOrder, SUM(OrderQty)
FROM
dbo.IopSalesPerf
WHERE
dbo.IopSalesPerf.CustRequestDate BETWEEN '2017-07-01' AND '2017-07-31'
AND EntrySystemTime = 1
AND Warehouse = '01'
AND StockCode = '001013'
GROUP BY
StockCode,SalesOrder
ORDER BY
StockCode ASC
SELECT
SalesOrder, SUM(NetSalesValue), SUM(QtyInvoiced)
FROM
ArTrnDetail
GROUP BY
SalesOrder
I would like to LEFT JOIN the last table onto the first using SalesOrder as the joining column. Can anyone assist with the syntax?
Simpliest way would be:
SELECT * FROM
(
SELECT StockCode,SalesOrder,sum(OrderQty)
FROM dbo.IopSalesPerf
WHERE dbo.IopSalesPerf.CustRequestDate between '2017-07-01' and '2017-07-31'
and EntrySystemTime = 1 and Warehouse = '01' and StockCode = '001013'
GROUP BY StockCode,SalesOrder
Order BY StockCode ASc
) AS A
LEFT JOIN
(
SELECT SalesOrder,sum(NetSalesValue),sum(QtyInvoiced)
FROM ArTrnDetail
Group by SalesOrder
) AS B
ON A.SalesOrder = B.SalesOrder

What is the correct way to find if any item in a one to many relationship has a flag set?

If I have the following database schema
And I wanted to write a query that listed all Bill_Items, but included a flag that was true if any associated inventory items where marked with the CheckFlag bit, what would be the correct way to do it?
The way I thought of doing it is
select *, case when exists(select CheckFlag
from inventoryitems
inner join Linked_items on Item_code = ItemCode
where Bill_Code = BillCode
and CheckFlag = 1
)
then 1 else 0 end as flagSet
from Bill_items
However I am fairly certain I am not doing this the correct way, what is the way I should be doing a check like this?
Try this:
select
Bill_items.billcode
,max(cast(CheckFlag as int)) as flagSet
from Bill_items
join Linked_items
on Bill_items.billcode = Linked_items.Bill_Code
join inventoryitems
on Linked_items.Item_code = inventoryitems.ItemCode
group by
Bill_items.billcode
If you need to include Bill_items without inventoryitems try this:
select
Bill_items.billcode
,max(isnull(cast(CheckFlag as int),0)) as flagSet
from Bill_items
left join Linked_items
on Bill_items.billcode = Linked_items.Bill_Code
left join inventoryitems
on Linked_items.Item_code = inventoryitems.ItemCode
group by
Bill_items.billcode
Here is a derived version joined onto bill items
select
Bill_items.*
,isnull(_CheckFlags.flagSet,0) as flagSet
from Bill_items
left join (
select
Linked_items.Bill_Code
,max(cast(CheckFlag as int)) as flagSet
from Linked_items
join inventoryitems
on Linked_items.Item_code = inventoryitems.ItemCode
group by
Linked_items.Bill_Code
) _CheckFlags
on Bill_items.billcode = _CheckFlags.Bill_Code
Try this for method without CAST:
select
Bill_items.*
,isnull(_CheckFlags.flagSet,0) as flagSet
from Bill_items
left join (
select
Linked_items.Bill_Code
,CheckFlag as flagSet
from Linked_items
join inventoryitems
on Linked_items.Item_code = inventoryitems.ItemCode
where CheckFlag = 1
group by
Linked_items.Bill_Code
,CheckFlag
) _CheckFlags
on Bill_items.billcode = _CheckFlags.Bill_Code
This should work (the inner sub-query shall save you from adding all the columns in the group by clause) and would also be faster
SELECT
bi.*,
temp.checkflag
FROM
bill_items bi
LEFT JOIN (
SELECT
max(ii.checkflag) as checkflag,
li.bill_code
FROM
linked_items li
JOIN inventory_items ii ON ( ii.item_code = ii.itemCode AND ii.checkflag = 1 )
GROUP BY
li.bill_code
) as temp ON ( temp.bill_code = bi.billCode )
Is there a reason why you don't just join the tables and use a row_number to return the first row for each billcode:
select billcode, flagset
from
(
select b.*, -- replace this with your columns
i.checkflag flagSet,
row_number() over(partition by b.billcode order by i.CheckFlag desc) rn
from Bill_items b
left join Linked_items l
on l.Bill_Code = b.BillCode
left join inventoryitems i
on l.Item_code = i.ItemCode
) d
where rn = 1;
See SQL Fiddle with Demo
The CheckFlag is a bit so the result will be zero or one, there shouldn't be a need for the CASE.

How to have distinct data in SQL using MAX on inner joined tables

I used this code to get distinct columns according to the max update_date. But still I get about 4 or 5 status_ids for the same tel_number. I want the max update date to take only the last date...which is not currently done by my code. Can someone please help me
SELECT DISTINCT t.Tel_Number,
t.Entity_ID,
t.Datasource,
t.Datasource_Number,
t.UpdateDate,
t.DataDate,
t.Telephone_ID,
t.Status_Id,
t.DateInserted,
t.ProcessName,
c.Status_Id AS CurrentCe_Status_ID,
s.StatusType AS CurrentCe_StatusType,
s.Description AS CurrentCe_Status_Description,
MAX(c.Update_Date) AS CurrentCe_Status_Date
FROM
Wrk.dbo.tel_trsn t WITH (NOLOCK) INNER JOIN CrWec.dbo.teldet d WITH (NOLOCK)
ON d.Tel_Number = t.Tel_Number
AND d.Entity_Id = t.Entity_ID
INNER JOIN CrWec.dbo.status c WITH (NOLOCK)
ON c.Entity_Id = t.Entity_ID
INNER JOIN CrWec.dbo.statusType s WITH (NOLOCK)
ON s.Status_Id = c.Status_Id
GROUP BY t.Tel_Number,
t.Entity_ID,
t.Datasource,
t.Datasource_Number,
t.UpdateDate,
t.DataDate,
t.Telephone_ID,
t.Status_Id,
t.DateInserted,
t.ProcessName,
c.Status_Id,
s.StatusType,
s.Description
Since you didn't specify what any of the keys were, I did the best I could with the query. In reality, if your key values are just Tel_Number, Entity_ID, Datasource, then you'd only need to partition on those 3 columns in the ROW_NUMBER function (or however many is necessary).
;with MaxUpdateDate as (
SELECT t.Tel_Number,
t.Entity_ID,
t.Datasource,
t.Datasource_Number,
t.UpdateDate,
t.DataDate,
t.Telephone_ID,
t.Status_Id,
t.DateInserted,
t.ProcessName,
c.Status_Id AS CurrentCe_Status_ID,
s.StatusType AS CurrentCe_StatusType,
s.Description AS CurrentCe_Status_Description,
c.Update_Date AS CurrentCe_Status_Date,
ROW_NUMBER() OVER (
PARTITION BY
t.Tel_Number,
t.Entity_ID,
t.Datasource,
t.Datasource_Number,
t.UpdateDate,
t.DataDate,
t.Telephone_ID,
t.Status_Id,
t.DateInserted,
t.ProcessName,
c.Status_Id,
s.StatusType,
s.Description
ORDER BY
c.Update_Date DESC) as 'RowNum'
FROM
Wrk.dbo.tel_trsn t WITH (NOLOCK)
INNER JOIN CrWec.dbo.teldet d WITH (NOLOCK)
ON d.Tel_Number = t.Tel_Number
AND d.Entity_Id = t.Entity_ID
INNER JOIN CrWec.dbo.status c WITH (NOLOCK)
ON c.Entity_Id = t.Entity_ID
INNER JOIN CrWec.dbo.statusType s WITH (NOLOCK)
ON s.Status_Id = c.Status_Id
)
SELECT
*
FROM
MaxUpdateDate
WHERE
RowNum = 1