Rookie mistake with join - sql-server

I believe I have made a rookie mistake with the last join but I cannot see what it is. The code worked until I needed to add the last entry recorded in the DONCOMMS table. (This table contains all the communications for each donor) Now I get 3 duplicate lines for every entry from the first table.
The original report shows me all the donor pledges from DON0010 and links the area description and donor from the tables AREA and DON0001 respectively. I now want to bring out the latest communication found in DONCOMMS for each pledge found in DON0010.
select
pl.[PLEDGE_NO]
,d1.[DONOR_NO]
,rtrim(pl.DONOR) DONOR
,d1.[CONTACT_NAME]
,d1.[AREA1]
,a1.[DESCRIPTION]
,pl.[AMOUNT]
,pl.[AMOUNT_RECVD]
,pl.[TYPE]
,ISNULL(t2.[COMM_TYPE],'') AS COMM_TYPE
,ISNULL(t2.[REFERENCE],'') AS REFERENCE
,ISNULL(t2.[DETAIL],'') AS DETAIL
,replace(isnull(convert(varchar, t2.[DATE], 103),''),'01/01/1753','') COMMS_DATE
,ISNULL(t2.[CONTACT],'') AS CONTACT
from don0010 pl
left join DON0001 d1 on pl.[DONOR] = d1.[DONOR]
left join AREA1 a1 on d1.[AREA1] = a1.[AREA]
**left join (Select * from doncomms dc where dc.DATE = (SELECT MAX(DATE) FROM doncomms WHERE DONOR_NO = dc.DONOR_NO) and ENTITY = 'C') t2
on t2.[DONOR_NO] = d1.[DONOR_NO]**

select
pl.[PLEDGE_NO]
,d1.[DONOR_NO]
,rtrim(pl.DONOR) DONOR
,d1.[CONTACT_NAME]
,d1.[AREA1]
,a1.[DESCRIPTION]
,pl.[AMOUNT]
,pl.[AMOUNT_RECVD]
,pl.[TYPE]
,ISNULL(t2.[COMM_TYPE],'') AS COMM_TYPE
,ISNULL(t2.[REFERENCE],'') AS REFERENCE
,ISNULL(t2.[DETAIL],'') AS DETAIL
,replace(isnull(convert(varchar, t2.[DATE], 103),''),'01/01/1753','') COMMS_DATE
,ISNULL(t2.[CONTACT],'') AS CONTACT
from don0010 pl
left join DON0001 d1 on pl.[DONOR] = d1.[DONOR]
left join AREA1 a1 on d1.[AREA1] = a1.[AREA]
outer apply
(
select top 1 with ties dc.*
from doncomms dc
where dc.DONOR_NO = d1.DONOR_NO and dc.ENTITY = 'C'
order by dc.DATE desc
) t2

The problem usually is you have several rows with DATE = MAX(DATE)
Try this query alone to find out if same DONOR_NO appear more than once
Select DONOR_NO, count(*)
from doncomms dc
where dc.DATE = (SELECT MAX(DATE)
FROM doncomms
WHERE DONOR_NO = dc.DONOR_NO)
and ENTITY = 'C'
GROUP BY DONOR_NO
In that case is better if you use a CTE syntaxis and ROW_NUMBER()
WITH cte as (
SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY DONOR_NO ORDER BY DATE DESC) as rn
FROM doncomms
WHERE ENTITY = 'C'
) T
WHERE T.rn = 1
)
select *
from don0010 pl
left join DON0001 d1 on pl.[DONOR] = d1.[DONOR]
left join AREA1 a1 on d1.[AREA1] = a1.[AREA]
left join cte as t2
on t2.[DONOR_NO] = d1.[DONOR_NO]

Thank you Juan, I just needed to add the field names to run as a view.
WITH cte as (
SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY DONOR_NO ORDER BY DATE DESC) as rn
FROM doncomms
WHERE ENTITY = 'C'
) T
WHERE T.rn = 1
)
select pl.[PLEDGE_NO]
,d1.[DONOR_NO]
,rtrim(pl.DONOR) DONOR
,d1.[CONTACT_NAME]
,d1.[AREA1]
,a1.[DESCRIPTION]
,pl.[AMOUNT]
,pl.[AMOUNT_RECVD]
,pl.[TYPE]
,pl.[SOURCE]
,d1.[POST_CODE] as DONOR_POSTCODE
,replace(isnull(convert(varchar, pl.PLEDGE_DATE, 103),''),'01/01/1753','') PLEDGE_DATE
,replace(isnull(convert(varchar, pl.[EVENT_DATE], 103),''),'01/01/1753','') EVENT_DATE
,rtrim(pl.DESCRIPTION1) DESCRIPTION1
,rtrim(pl.DESCRIPTION2) DESCRIPTION2
,pl.[COMPLETE]
,pl.[APPLICATION]
,rtrim(pl.COMMENT1) COMMENT1
,rtrim(pl.COMMENT2) COMMENT2
,pl.[MEMO]
,ISNULL(t2.[ENTERED_BY] , '') AS ENTERED_BY
,ISNULL(t2.[COMM_TYPE],'') AS COMM_TYPE
,ISNULL(t2.[REFERENCE],'') AS REFERENCE
,ISNULL(t2.[DETAIL],'') AS DETAIL
,replace(isnull(convert(varchar, t2.[DATE], 103),''),'01/01/1753','') COMMS_DATE
,ISNULL(t2.[CONTACT],'') AS CONTACT
from don0010 pl
left join DON0001 d1 on pl.[DONOR] = d1.[DONOR]
left join AREA1 a1 on d1.[AREA1] = a1.[AREA]
left join cte as t2
on t2.[DONOR_NO] = d1.[DONOR_NO]

Related

Calculate account receivables of Customers

I'm trying to calculate list all Customers A/C Receivables.
I calculate list of sales and receipts of customers, now I'm stuck how to calculate Receivables.
All Customer's Sales Report
Select c.StakeHolderId, c.CompanyName, sum(s.Amount) as TotalSales from
StakeHolders c
left Join Sales s on
c.StakeHolderId = s.BuyerId
where c.StakeHolderTypeId = '0b85a69e-55f2-4142-a49d-98e22aa7ca10'
group By c.StakeHolderId, c.CompanyName
All Customer's Receipts
Select c.StakeHolderId, c.CompanyName, sum(pr.Amount) as TotalReceipts
from
StakeHolders c
left Join PaymentsAndReceipts pr on
c.StakeHolderId = pr.StakeHolderId
where c.StakeHolderTypeId = '0b85a69e-55f2-4142-a49d-98e22aa7ca10'
group By c.StakeHolderId, c.CompanyName
I have tried this but didn't get the right result.
Select
c.StakeHolderId,
c.CompanyName,
sum(s.Amount) - sum(pr.Amount) as Receivables
from Sales s
right outer join StakeHolders c on
c.StakeHolderId = s.BuyerId
left outer join PaymentsAndReceipts pr on
pr.StakeHolderId = c.StakeHolderId
where c.StakeHolderTypeId = '0b85a69e-55f2-4142-a49d-98e22aa7ca10'
Group By c.StakeHolderId,c.CompanyName
expected Result:
Does this work for you?:
WITH [CalculatedData] AS
(
SELECT
C.[StakeHolderId],
C.[CompanyName],
COALESCE((SELECT SUM([Amount])
FROM [Sales]
WHERE [BuyerId] = C.[StakeHolderId]
), 0) AS [TotalSales],
COALESCE((SELECT SUM([Amount])
FROM [PaymentsAndReceipts]
WHERE [StakeHolderId] = C.[StakeHolderId]
), 0) AS [TotalReceipts]
FROM
[StakeHolders] AS C
WHERE
C.[StakeHolderTypeId] = '0b85a69e-55f2-4142-a49d-98e22aa7ca10'
)
SELECT
[StakeHolderId],
[CompanyName],
[TotalSales] - [TotalReceipts] AS [Receivables]
FROM
[CalculatedData]
Note that I include negative values in the result. If you want negative values shown between parentheses, that's possible too, but it will require conversion of numerical data to textual data in the query results. IMHO, that's not a flexible strategy (since you lose the option to perform any additional client-side calculations) and it should be the client's purpose to correctly format the values.
Edit:
If you don't like Common Table Expressions, you can convert it to a regular table expression:
SELECT
[StakeHolderId],
[CompanyName],
[TotalSales] - [TotalReceipts] AS [Receivables]
FROM
(
SELECT
C.[StakeHolderId],
C.[CompanyName],
COALESCE((SELECT SUM([Amount])
FROM [Sales]
WHERE [BuyerId] = C.[StakeHolderId]
), 0) AS [TotalSales],
COALESCE((SELECT SUM([Amount])
FROM [PaymentsAndReceipts]
WHERE [StakeHolderId] = C.[StakeHolderId]
), 0) AS [TotalReceipts]
FROM
[StakeHolders] AS C
WHERE
C.[StakeHolderTypeId] = '0b85a69e-55f2-4142-a49d-98e22aa7ca10'
) AS [CalculatedData]
just take first query LEFT JOIN to second query join by StakeHolderId & companyname. After that take sales subtract receipts
SELECT S.StakeHolderId, S.CompanyName,
Receivables = TotalSales - ISNULL(TotalReceipts , 0)
FROM
(
-- this is your first query
Select c.StakeHolderId, c.CompanyName, sum(s.Amount) as TotalSales from
StakeHolders c
left Join Sales s on
c.StakeHolderId = s.BuyerId
where c.StakeHolderTypeId = '0b85a69e-55f2-4142-a49d-98e22aa7ca10'
group By c.StakeHolderId, c.CompanyName
) S
LEFT JOIN
(
-- this is your second query
Select c.StakeHolderId, c.CompanyName, sum(pr.Amount) as TotalReceipts
from
StakeHolders c
left Join PaymentsAndReceipts pr on
c.StakeHolderId = pr.StakeHolderId
where c.StakeHolderTypeId = '0b85a69e-55f2-4142-a49d-98e22aa7ca10'
group By c.StakeHolderId, c.CompanyName
) R ON S.StakeHolderId = R.StakeHolderId
AND S.CompanyName = R.CompanyName

Is there a way to UPDATE TOP (N) with inner join where N is a field of such inner join?

I'm trying to create a script that synchronizes Sales and Inventory tables. For that I wrote an UPDATE on the Inventory table (which has 1 record per item of inventory present) like this:
UPDATE TOP (q.QuantitySold) i
SET i.Converted = 1,
i.CartID = q.CartID,
i.ReservedDate = GETDATE()
FROM Inventory i
INNER JOIN
(
SELECT product.ProductID, sales.CartID, COUNT(sales.ID) AS QuantitySold
FROM Products product
INNER JOIN Sales sales ON sales.ProductID = product.ProductID
WHERE <conditions>
GROUP BY product.ProductID, sales.CartID
) q ON q.ProductID = i.ProductID
WHERE i.Converted = 0 AND i.CartID IS NULL
But it's not working, error says q.QuantitySold couldn't be bound.
Is there a way to update N records of inventory (equal to the quantity sold) without using a cursor? I refuse to give up like that.
Note: this is a simplified version of the actual query.
You could use ROW_NUMBER to enumerate the inventory items that you need to update.
WITH cteProducts AS(
SELECT product.ProductID, sales.CartID, COUNT(sales.ID) AS QuantitySold
FROM Products product
INNER JOIN Sales sales ON sales.ProductID = product.ProductID
WHERE <conditions>
GROUP BY product.ProductID, sales.CartID
),
cteInventory AS(
SELECT *,
ROW_NUMBER() OVER( PARTITION BY ProductID ORDER BY (SELECT NULL)) AS rn /*Change the ORDER BY for an actual column if needed, probably for FIFO*/
FROM Inventory
WHERE i.Converted = 0
AND i.CartID IS NULL
)
UPDATE i
SET i.Converted = 1,
i.CartID = q.CartID,
i.ReservedDate = GETDATE()
FROM cteInventory i
INNER JOIN cteProducts q ON q.ProductID = i.ProductID
WHERE i.rn <= q.QuantitySold;

sql - retrieve only first record using join

I have three tables: Employee, Contact, Employee_Contact
Employee:
EMP_ID, FIRST_NAME, LAST_NAME...
CONTACT:
CONTACT_ID, TELEPHONE, MOBILE...
EMPLOYEE_CONTACT:
EMP_ID, CONTACT_ID
Employee and Contact table is mapped with Employee_Contact table.
So I want to retrieve all Employees with Employee's only first contact, I tried this query but it is retrieving all the contacts related to that employee. Please check below query and let me know in case of any mistakes.
SELECT EMP.FirstName
,DEPT.Description AS Department
,CNT.Mobile
,CNT.Telephone
FROM MstEmp EMP
LEFT OUTER JOIN Department DEPT ON EMP.DeptID = DEPT.Department_ID
LEFT OUTER JOIN Employee_Contact EC ON EMP.EMP_ID = EC.EMP_ID
OUTER APPLY (
SELECT TOP 1 *
FROM Contact CONT
WHERE EC.Contact_ID = CONT.Contact_ID
) CNT
Use Top keyword retrieve top one record.
SELECT TOP 1 EMP.FirstName, DEPT.Description AS Department, CNT.Mobile,
CNT.Telephone
FROM MstEmp EMP
LEFT OUTER JOIN Department DEPT on EMP.DeptID = DEPT.Department_ID
LEFT OUTER JOIN Employee_Contact EC ON EMP.EMP_ID = EC.EMP_ID
OUTER APPLY(SELECT TOP 1 * FROM Contact CONT WHERE EC.Contact_ID =
CONT.Contact_ID)CNT
Try this one:
SELECT EMP.FirstName
,DEPT.Description AS Department
,t.Mobile
,t.Telephone
FROM MstEmp EMP
LEFT OUTER JOIN Department DEPT ON EMP.DeptID = DEPT.Department_ID
LEFT OUTER JOIN Employee_Contact EC ON EMP.EMP_ID = EC.EMP_ID
LEFT OUTER JOIN Contact CONT ON EC.Contact_ID = (
SELECT MIN(c.Contact_ID) c_id
FROM Contact c
WHERE CONT.Contact_ID = c.Contact_ID
) t
Give it a try:
select * from (
select E.[emp_id],
E.[first_name],
E.[last_name],
C.*,
RANK() over (partition by (E.[emp_id], E.[first_name], E.[last_name]) order by (select null)) [RN]
from Employee [E]
join employee_contact [EC] on E.[emp_id] = EC.[emp_id]
join contact [C] on EC.[contact_id] = C.[contact_id]
) A where RN = 1
You can use ROW_NUMBER() and get the first record for all the employees and then join it with the other tables.
SELECT EMP.FirstName,
DEPT.Description AS Department,
c.Mobile,
c.Telephone
FROM MstEmp EMP
LEFT OUTER JOIN Department DEPT ON EMP.DeptID = DEPT.Department_ID
INNER JOIN (
SELECT *,ROW_NUMBER() OVER (PARTITION BY EMP_ID ORDER BY EMP_ID) AS RN
FROM Employee_Contact ) EC ON EMP.EMP_ID = EC.EMP_ID
INNER JOIN Contact c ON c.Contact_ID=EC.Contact_ID
WHERE EC.RN=1

Top N percent Desc and Top M percent Asc

I am trying to get top 5 customertypes and show data for each 5 customer types, The balance (which can be any amount) I show them as "Other Customer Types". my issue is since the rows can be random and not perfectly divisible by a number then there can be repeated values in the top 5 showing up in the "Other" group which overstates the Total sales.
the Data is also being rendered in SSRS
My code using TOP PERCENT:
select final.[description], sum(final.YTDSales$) as YTDSales$
FROM(
select top 25 percent pytd2.[Description], sum(pytd2.YTDSales$) as YTDSales$
FROM(
-- ytd sales
select re.SIC_Desc as [description], sum((ol.NetAmt - ol.WhlOrdDiscAmt) / #exrt) AS YTDSales$
from dbo.order_line_invoice ol
INNER JOIN dbo.Vendor vd ON ol.Cono = vd.Cono AND vd.VendId = ol.VendId
inner join Product_Warehouse pw on ol.ProdId = pw.prodid and ol.WhseId = pw.whseid and ol.cono = pw.cono
inner join Customer c on ol.custId = c.CustId and ol.Cono = c.Cono
left join MDData.dbo.RetailEnvironment re on c.SIC = re.SIC
where ol.InvoiceDate BETWEEN #FStartDate AND #EndDate AND ol.Cono = 1 and ol.VendId IN(#Vendid) and ol.prodcatid NOT LIKE 'GP%'
group by re.SIC_Desc
)PYTD2
group by pytd2.[description]
order by sum(pytd2.YTDSales$) DESC
UNION ALL
select top 75 percent 'Other' as 'description', sum(pytd.YTDSales$) as YTDSales$
FROM(
-- ytd sales
select re.SIC_Desc as [description], sum((ol.NetAmt - ol.WhlOrdDiscAmt) / #exrt) AS YTDSales$
from dbo.order_line_invoice ol
INNER JOIN dbo.Vendor vd ON ol.Cono = vd.Cono AND vd.VendId = ol.VendId
inner join Product_Warehouse pw on ol.ProdId = pw.prodid and ol.WhseId = pw.whseid and ol.cono = pw.cono
inner join Customer c on ol.custId = c.CustId and ol.Cono = c.Cono
left join MDData.dbo.RetailEnvironment re on c.SIC = re.SIC
where ol.InvoiceDate BETWEEN #FStartDate AND #EndDate AND ol.Cono = 1 and ol.VendId IN(#Vendid) and ol.prodcatid NOT LIKE 'GP%'
group by re.SIC_Desc
)PYTD
group by Ppytd.[description]
order by sum(pytd.YTDSales$)
)final
group by final.[Description]
order by sum(final.YTDSales$) DESC
my results:
As you can see the Large Independent and Other has the same figure of $2280.60 in YTDQty since it is being repeated
I was picturing something like this:
with data as (
-- your base query here grouped and summarized by customer type
), rankedData as (
select *, row_number() over (order by YTDSales$ desc) as CustTypeRank
from data
)
select
case when CustTypeRank <= 5 then min("description") else 'Others' end as "description",
sum(YTDSales$) as YTDSales$
from rankedData
group by case when CustTypeRank <= 5 then CustTypeRank else 999 end
order by case when CustTypeRank <= 5 then CustTypeRank else 999 end
I actually used RANK instead which worked great :-
select 0 as rankytd, RANK() OVER(ORDER BY sum(ol.NetAmt - ol.WhlOrdDiscAmt) DESC) as rankpytd, re.sic, ol.VendId, vd.name, re.SIC_Desc As [description], 0 AS YTDQty, sum(ol.Quantity) AS PYTDQty
from dbo.order_line_invoice ol
INNER JOIN dbo.Vendor vd ON ol.Cono = vd.Cono AND vd.VendId = ol.VendId
inner join dbo.Product p on ol.Cono = p.Cono and ol.prodid = p.ProdId and p.ProdCatId in (#pcat)
inner join Product_Warehouse pw on ol.ProdId = pw.prodid and ol.WhseId = pw.whseid and ol.cono = pw.cono
inner join Customer c on ol.custId = c.CustId and ol.Cono = c.Cono
left join MDData.dbo.RetailEnvironment re on c.SIC = re.SIC
where ol.InvoiceDate BETWEEN DATEADD(YEAR, -1,#FStartDate) AND DATEADD(YEAR, -1, #EndDate) and ol.Cono = 1 and ol.VendId IN(#Vendid) and ol.prodcatid NOT LIKE 'GP%'
group by re.sic, ol.VendId, vd.Name, re.SIC_Desc

select count over partition by

I am learning window functions in sql server. I am using AdventrueWorks2012 database for practice. I want to calculate total number of sales and purchases for each item in the store.
The classic solution can be like
SELECT ProductID,
Quantity,
(SELECT Count(*)
FROM AdventureWorks.Purchasing.PurchaseOrderDetail
WHERE PurchaseOrderDetail.ProductID = p.ProductID) TotalPurchases,
(SELECT Count(*)
FROM AdventureWorks.Sales.SalesOrderDetail
WHERE SalesOrderDetail.ProductID = p.ProductID) TotalSales
FROM (SELECT DISTINCT ProductID,
Quantity
FROM AdventureWorks.Production.ProductInventory) p
Trying to convert to window functions gives me wrong results:
SELECT DISTINCT d.ProductID,
Quantity,
Count(d.ProductID)
OVER(
PARTITION BY d.ProductID) TotalPurchases,
Count(d2.ProductID)
OVER(
PARTITION BY d2.ProductID) TotalSales
FROM (SELECT DISTINCT ProductID,
Quantity
FROM AdventureWorks.Production.ProductInventory) p
INNER JOIN AdventureWorks.Purchasing.PurchaseOrderDetail d
ON p.ProductID = d.ProductID
INNER JOIN AdventureWorks.Sales.SalesOrderDetail d2
ON p.ProductID = d2.ProductID
ORDER BY d.ProductID
Why this is wrong? How can I correct it?
You should change INNER JOIN to LEFT JOIN
Because when you inner join, result will miss productid which from ProductInventory table does not have PurchaseOrderDetail or SalesOrderDetail.

Resources