Data from 3 tables with a primary key in common - sql-server

I have three tables Invoice, Account and Visits
Each has a primary key in common (AccountID). I need to draw data for accounts that have not had a visit in the month of july and had a valid account to allow a visit in that month (drawn from Invoice - scheduletype and Account - startdate)
I have tried using several joins but am not able to get the data out as I need it and am fairly new to SQL. Would anyone be able to help me out with some pointers please?

Guessing with the Visit date,
SELECT a.AccountID
FROM Account a
WHERE NOT EXISTS (
SELECT 1
FROM Invoice i
WHERE i.AccountID = a.AccountID
AND i.ScheduleType = 'hold'
AND i.StartDate < '8/1/2012'
AND i.EndDate >= '7/1/2012'
)
AND NOT EXISTS (
SELECT 1
FROM Visits v
WHERE v.AccountID = a.AccountID
AND v.SessionDate >= '7/1/2012'
AND v.SessionDate < '8/1/2012'
)
AND a.StartDate < '8/1/2012'
AND a.EndDate >= '7/1/2012'
GO

select A.*
from Accounts A
where A.AccountID NOT IN
(Select V.AcocountID
From Visits V
Where V.date like '%July%' //replace by appropriate condition
) AND
A.AccountID IN
(Select I.AccountID
From Invoice I
Where I.type = Invoice //replace by appropriate condition
) AND
A.startDate< July //replace by appropriate condition

Borrowing from the previous answer, you can do it like this too (I prefer using EXISTS to see if the AccountID is in there - if there are any, it stops looking immediately).
SELECT A.* FROM Accounts A WHERE A.AccountID NOT IN
(SELECT V.AccountID
FROM Visits V
WHERE CONVERT(date, V.date) BETWEEN '2012-07-01' AND '2012-07-31'
-- assuming V.date is a 'datetime'. if V.date is already a 'date' then:
-- WHERE V.date BETWEEN '2012-07-01' AND '2012-07-31'
-- or you could say
-- WHERE NOT(DATEPART(month, V.date) = '7' AND DATEPART(year, V.date) = '2012')
)
AND EXISTS (SELECT *
FROM Invoice I
WHERE I.type = Invoice //or whatever
) AND
A.startDate < '2012-07-01' //or whatever

Use below query :
Select A.*
From Accounts A
left join visits v on v.AccountID = A.AccountID
AND v.SessionDate >= '2012-07-01'
AND v.SessionDate < '2012-08-01'
left join Invoice I ON I.AccountID = A.AccountID
AND I.ScheduleType = 'hold'
Where V.AccountID IS NULL
and I.AccountID IS NULL
and A.startDate < '2012-07-01'

Related

Nested SQL select statement in SQL Server with aggregate function

I have a query where first table Project will be fetched as a list. So every project row should have a unique ProjectID.
Now the second table Contract should get one row for each project. Although there are multiple rows for some projects in the Contract table. But I have to get only one row based on the select count condition I have applied in the below query.
Right now my code is returning two rows and I want only the second row not first row. Logic is basically only Approve 0 & 1 will be picked which cannot be more than one row together. But value 2 is multiple and destroying my result set. But for multiple projects of different ProjectId is should return multiple results.
SELECT
a.ProjectId
,a.Title
,a.CreationDate
,a.Status
,DATEDIFF(second, CAST(SWITCHOFFSET(SYSDATETIMEOFFSET(), '+05:30') AS DATETIME), BidEndDateTime) / 3600 As BidTimeLeft
,(SELECT COUNT(*) FROM Bidding b WHERE a.ProjectId = b.ProjectId) AS BidsCount
,(SELECT e.CompanyName FROM Bidding b inner join Partner e on b.PartnerId = e.PartnerId WHERE a.ProjectId = b.ProjectId and b.BidAccepted = 1) AS BidSelected
--,h.CompanyName
--Contact table column ApprovedbyCustomer will only have one 0 or 1 for same project, Partner can only create contract either for the first time or if it is rejected by Customer
,(SELECT COUNT(*) FROM Contract x WHERE a.ProjectId = x.ProjectId and x.ApprovedbyCustomer in (0,1) ) AS ContractCount
,g.ContractId
--,(SELECT c.ContractId FROM Contract c WHERE a.ProjectId = c.ProjectId and c.ApprovedbyCustomer in (1,2)) AS ContractId
,g.ProjectValue
, g.Duration
, (CASE g.DurationType WHEN 1 THEN 'Days' WHEN 2 THEN 'Weeks' ELSE 'Months' END) As DurationType
, g.StartDate
, g.EndDate
, g.ApprovedByCustomer
--0 - No Action, 1- Accepted, 2- Send Back
,(SELECT COUNT(*) FROM PaymentRequest e WHERE a.ProjectId = e.ProjectId) AS PaymentCount
FROM
Project a
LEFT JOIN
Contract g ON a.ProjectId = g.ProjectId
-- LEFT JOIN Partner h ON g.PartnerId = h.PartnerId
WHERE
a.CustomerId = 11111;
Try a subquery with ROW_NUMBER() OVER (PARTITION BY a.ProjectId ORDER BY g.ApprovedByCustomer) AS RowNum. In the outer query, add the criteria WHERE RowNum = 1:
SELECT *
FROM
(
SELECT
ROW_NUMBER() OVER (PARTITION BY a.ProjectId ORDER BY g.ApprovedByCustomer) AS RowNum
,a.ProjectId
,a.Title
,a.CreationDate
,a.Status
,DATEDIFF(second, CAST(SWITCHOFFSET(SYSDATETIMEOFFSET(), '+05:30') AS DATETIME), BidEndDateTime) / 3600 As BidTimeLeft
,(SELECT COUNT(*) FROM Bidding b WHERE a.ProjectId = b.ProjectId) AS BidsCount
,(SELECT e.CompanyName FROM Bidding b inner join Partner e on b.PartnerId = e.PartnerId WHERE a.ProjectId = b.ProjectId and b.BidAccepted = 1) AS BidSelected
--,h.CompanyName
--Contact table column ApprovedbyCustomer will only have one 0 or 1 for same project, Partner can only create contract either for the first time or if it is rejected by Customer
,(SELECT COUNT(*) FROM Contract x WHERE a.ProjectId = x.ProjectId and x.ApprovedbyCustomer in (0,1) ) AS ContractCount
,g.ContractId
--,(SELECT c.ContractId FROM Contract c WHERE a.ProjectId = c.ProjectId and c.ApprovedbyCustomer in (1,2)) AS ContractId
,g.ProjectValue
, g.Duration
, (CASE g.DurationType WHEN 1 THEN 'Days' WHEN 2 THEN 'Weeks' ELSE 'Months' END) As DurationType
, g.StartDate
, g.EndDate
, g.ApprovedByCustomer
--0 - No Action, 1- Accepted, 2- Send Back
,(SELECT COUNT(*) FROM PaymentRequest e WHERE a.ProjectId = e.ProjectId) AS
PaymentCount
FROM
Project a
LEFT JOIN
Contract g ON a.ProjectId = g.ProjectId
-- LEFT JOIN Partner h ON g.PartnerId = h.PartnerId
WHERE
a.CustomerId = 11111
) OrderedProjects
WHERE RowNum = 1;
You need to add the filter to the main query:
and g.ApprovedbyCustomer in (0,1)

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 Query Group by Count and Left Join Tables

i need your help! I got some simple SQL skills, but this query kills me...
My Tables
Now i want the TOP5 WorkTimes on the Equipment (What Equipment got the longest WorkTime).
I want this OUTPUT:
MY Query:
SELECT
Equipment, EquipmentName, count(Equipment) as Count
FROM
Operations o
LEFT JOIN Orders ord ON ord.Id = o.[Order]
LEFT OUTER JOIN Equipments e ON ord.Equipment = e.EquipmentNumber
GROUP BY
Equipment, EquipmentName
ORDER BY Count DESC;
Another Question is how i can show o.Worktime?
i got an error with GroupBy...
please help me Thanks!
You can try this query:
select equip_nr,
(select equipmentname from table_equipments where equipmentnr = [to].equip_nr) equip_name,
sum(timeInMins) / 60.0 Worktime
from (
select (select equipmentnr from table_orders where id = [to].[order]) equip_nr,
case when workunittime = 'RH' then worktime * 60 else worktime end timeInMins
from table_operations [to]
where exists(select 1 from table_orders
where [to].[order] = id
and location = '152')
and [start] >= '2018-07-01 00:00:00.000' and [start] < '2018-08-01 00:00:00.000'
) [to] group by equip_nr
By the way, LEFT JOIN is equivalent to LEFT OUTER JOIN.
Just use SUM(worktime) as aggregate function, instead of COUNT(Equipment)
SELECT
e.[ID_Equipment]
, Name
, SUM( IIF(o.WorkUnitTime='MIN', worktime/60.0, worktime) ) as WorktimeMIN
FROM
Operations o
LEFT JOIN Orders ord ON ord.ID_Order = o.ID_Order
LEFT OUTER JOIN Equipment e ON ord.ID_Equipment = e.ID_Equipment
GROUP BY
e.[ID_Equipment]
, Name
ORDER BY
WorktimeMIN DESC
See SQL Fiddle here: http://sqlfiddle.com/#!18/5b5ed/11

Sum of missing data

The below query displays sites against the total orders within last week.
But if there is no order for a given site in last week, i should still see the site with a sum of zero.
At the moment its only giving me four sites, thats because no order has been made in the last week for those sites.
select SITE
,SUM(Case When OrderDate >= dateadd(dd,(datediff(dd,-53690,getdate()-1)/7)*7,-53690)
Then 1
Else 0
End) as COMPLETED
from
(
SELECT DISTINCT ORDERS.SITE, ORDERS.ORDERDATE FROM ORDERS
INNER JOIN PHONEDATA AS P
ON ORDERS.RECID = P.OrderID
where SITE IN ('SITE1','SITE2','SITE3','SITE4','SITE5','SITE6','SITE7')
) X
GROUP BY SITE
order by SITE
RESULT:
Site---------------------Completed
SITE1-----------------------2
SITE2-----------------------2
SITE3-----------------------2
SITE4-----------------------2
EXPECTED RESULT:
Site---------------------Completed
SITE1-----------------------2
SITE2-----------------------2
SITE3-----------------------2
SITE4-----------------------2
SITE5-----------------------0
SITE6-----------------------0
SITE7-----------------------0
updated:
select SITE
,SUM(Case When OrderDate >= dateadd(dd,(datediff(dd,-53690,getdate()-1)/7)*7,-53690)
Then 1
Else 0
End) as COMPLETED
from
(
SELECT DISTINCT ORDERS.SITE, ORDERS.ORDERDATE FROM ORDERS
where SITE IN ('SITE1','SITE2','SITE3','SITE4','SITE5','SITE6','SITE7')
) X
GROUP BY SITE
order by SITE
I have now removed the inner join with phone data table, so i am now getting the missing sites. but the reason i avoided this approach is because if i only rely on the orders table the orderdate time field is inserted few times for a given order, and the final order makes it to the phonedata table, so now i get more values in completed count but it should only consider the latest value for each day for each site
result of update :
Site---------------------Completed
SITE1-----------------------5
SITE2-----------------------5
SITE3-----------------------5
SITE4-----------------------5
SITE5-----------------------0
SITE6-----------------------0
SITE7-----------------------0
expected
Site---------------------Completed
SITE1-----------------------2
SITE2-----------------------2
SITE3-----------------------2
SITE4-----------------------2
SITE5-----------------------0
SITE6-----------------------0
SITE7-----------------------0
If there are no rows in the table with the sites that have no orders, how can it return any rows to count? Perhaps you have a table with all the possible sites that can be joined to? Or create a temp table with the site values. You could then left join the orders table to this. i.e.
create table #sites (site varchar(25));
insert into #sites values ('SITE1','SITE2','SITE3','SITE4','SITE5','SITE6','SITE7');
...
from
(
SELECT DISTINCT ORDERS.SITE, ORDERS.ORDERDATE FROM
#sites s left join ORDERS on orders.site = s.site
INNER JOIN PHONEDATA AS P
ON ORDERS.RECID = P.OrderID
) X
...
Try using a left join instead of the inner join. It is probably not getting rows from the phone data table:
select SITE
,SUM(Case When OrderDate >= dateadd(dd,(datediff(dd,-53690,getdate()-1)/7)*7,-53690)
Then 1
Else 0
End) as COMPLETED
from
(
SELECT DISTINCT ORDERS.SITE, ORDERS.ORDERDATE FROM ORDERS
Left JOIN PHONEDATA AS P
ON ORDERS.RECID = P.OrderID
where SITE IN ('SITE1','SITE2','SITE3','SITE4','SITE5','SITE6','SITE7')
) X
GROUP BY SITE
order by SITE
It'd be best to start with a "Site" table and then left join to your results. This example mimics the behavior, and can be used as a hack-workaround.
DECLARE #table TABLE
(
site VARCHAR(10) ,
Completed TINYINT
)
INSERT INTO #table
( site, Completed )
VALUES ( 'SITE1', 0 ),
( 'SITE2', 0 ),
( 'SITE3', 0 ),
( 'SITE4', 0 ),
( 'SITE5', 0 ),
( 'SITE6', 0 ),
( 'SITE7', 0 )
WITH cte
AS ( SELECT SITE ,
SUM(CASE WHEN OrderDate >= DATEADD(dd,( DATEDIFF(dd, -53690, GETDATE() - 1) / 7 ) * 7, -53690)
THEN 1
ELSE 0
END) AS COMPLETED
FROM ( SELECT DISTINCT
ORDERS.SITE ,
ORDERS.ORDERDATE
FROM ORDERS
INNER JOIN PHONEDATA AS P ON ORDERS.RECID = P.OrderID
WHERE SITE IN ( 'SITE1', 'SITE2', 'SITE3',
'SITE4', 'SITE5', 'SITE6',
'SITE7' )
)
GROUP BY SITE
)
SELECT t.site ,
t.completed + cte.COMPLETED
FROM #table t
LEFT OUTER JOIN cte ON t.site = cte.Site
ORDER BY t.site

Resources