How to SUM Only One Time Per UniqueId in SQL? - sql

I have two tables that look roughly like this:
Table A
DocumentId (*is unique) DocumentDate
1 2016-01-01
2 2016-01-01
3 2016-02-01
4 2016-03-01
and Table B
ContractId SnapshotTimeId NetFinanced
1 20160231 300
1 20160331 300
1 20160431 300
2 20160231 450
2 20160331 450
2 20160431 450
3 20160331 500
3 20160431 500
4 20160431 150
I would like the final table to look something like this:
DocumentDate NetFinanced
2016-01-01 750
2016-02-01 500
2016-03-01 150
I have tried the following and it doesn't work:
SELECT A.DocumentDate, SUM(B.NetFinanced)
FROM A
JOIN B on B.ContractId=A.DocumentId
GROUP BY A.DocumentDate
Any ideas? Thanks in advance

you can use distinct
SELECT A.DocumentDate,
SUM(B.NetFinanced)
FROM A
JOIN (SELECT DISTINCT
ContractId,
NetFinanced
FROM B
) B ON B.ContractId = A.DocumentId
GROUP BY A.DocumentDate
the result of this will be different if the NetFinanced amount changes per SnapshotTimeId
if you want the most recent NetFinanced amount, you can use Row_number() to order the values.
SELECT A.DocumentDate,
SUM(B.NetFinanced)
FROM A
JOIN (SELECT ROW_NUMBER() OVER (PARTITION BY ContractId ORDER BY SnapshotTimeId DESC) Rn,
ContractId,
NetFinanced
FROM B
) B ON B.ContractId = A.DocumentId AND B.Rn = 1
GROUP BY A.DocumentDate

You have duplicate values for NetFinanced in TableB, of course the results won't give you what you want. You need to join TableA with the unique values (I assume) of ContractId and NetFinanced columns from TableB:
SELECT A.DocumentDate,
SUM(B.NetFinanced) NetFinanced
FROM dbo.TableA A
INNER JOIN (SELECT DISTINCT ContractId, NetFinanced
FROM dbo.TableB) B
ON A.DocumentId = B.ContractId
GROUP BY A.DocumentDate;

Try Like this
SELECT A.DocumentDate, SUM(B.NetFinanced)
FROM A
JOIN (SELECT MAX(ContractId) ContractId, MAX(SnapshotTimeId)SnapshotTimeId,
MAX(NetFinanced)NetFinanced
FROM B GROUP BY ContractId) B ON B.ContractId = A.DocumentId
GROUP BY A.DocumentDate

Related

SQL Server : smallest ROW_NUM in where condition, with subgroup pre-condition

Thanks all in advance! I am trying to describe this as clear as I can.
I got two sub-tables, 1st table retrieves Comfirmed_Date and the 2nd table retrieves Mail_Date with condition Mail_Date >= Comfirmed_Date.
select
a.ID
,g.ROWNUM
,f.CORM_DT
,g.MAIL_DT
from
SOURCE_U a
left join
(select
a.SOURCE_ID
, Max(Cast(b.ATUF_DATE3 as date)) as [CORM_DT]
from
ATTACH_U a
inner join
USERFLD_D b on a.DEST_CK = b.DEST_CK
group by
a.SOURCE_ID) f on f.SOURCE_ID = a.SOURCE_ID
left join
(select
a.SOURCE_ID
, cast(b.MAILED_DT as date) as MAIL_DT
, row_number() over (partition by SOURCE_ID order by CREATE_DT) as ROWNUM
from
ATTACH_U a
left join
LETTER_D b on b.DEST_CK = a.DEST_CK) g on g.SOURCE_ID = a.SOURCE_ID
and g.MAIL_DT >= f.CORM_DT
I need the first line (smallest row_num) for the tables, how can I achieve that?
Original I think I can make condition like
where g.ROWNUM = 1
but because I have the condition on joint table, it does not work for below situations.
ID gROWNUM CORM_DT MAIL_DT
1001 3 2020-10-20 2020-10-22
1001 4 2020-10-20 2020-10-30
1002 2 2020-10-20 2020-10-21
1002 3 2020-10-20 2020-10-23
1002 4 2020-10-20 2020-10-28
1003 1 2020-10-20 2020-10-30
1004 1 2020-10-20 2020-10-21
1004 2 2020-10-20 2020-10-23
1005 4 2020-10-20 2020-10-28
1006 1 2020-10-20 2020-10-30
I only want one line for each ID here.
Try this:
SELECT TOP 1
a.ID
, g.ROWNUM
, f.CORM_DT
, g.MAIL_DT
FROM SOURCE_U a
LEFT JOIN (
SELECT
a.SOURCE_ID
, Max(Cast(b.ATUF_DATE3 as date)) as [CORM_DT]
FROM ATTACH_U a
INNER JOIN USERFLD_D b
ON a.DEST_CK = b.DEST_CK
GROUP BY a.SOURCE_ID
) f
ON f.SOURCE_ID = a.SOURCE_ID
LEFT JOIN (
SELECT
a.SOURCE_ID
, CAST( b.MAILED_DT AS date) AS MAIL_DT
, ROW_NUMBER() OVER( PARTITION BY SOURCE_ID ORDER BY CREATE_DT ) AS ROWNUM
FROM ATTACH_U a
LEFT JOIN LETTER_D b
ON b.DEST_CK = a.DEST_CK
) g
ON g.SOURCE_ID = a.SOURCE_ID
AND g.MAIL_DT >= f.CORM_DT
ORDER BY
g.ROWNUM;
All you need is a window function in your select.
select rows, columns... from (
select dense_rank() over ( partition by a.ID order by MAIL_DT) as rows, columns...
...
)
where rows = 1

SQL Select Distinct Records From Two Tables

I am trying to write a SQL statement that will return a set of Distinct set of CompanyNames from a table based on the most recent SaleDate withing a specified date range from another table.
T01 = Account
T02 = TransHeader
The fields of importance are:
T01.ID, T01.CompanyName
T02.AccountID, T02.SaleDate
T01.ID = T02.AccountID
What I want to return is the Max SaleDate for each CompanyName without any duplicate CompanyNames and only the Max(SaleDate) as LastSale. I will be using a Where Clause to limit the SaleDate range.
I tried the following but it returns all the records for all SalesDates in the range. This results in the same company being listed multiple times.
Current MS-SQL Query
SELECT T01.CompanyName, T02.LastSale
FROM
(SELECT DISTINCT ID, IsActive, ClassTypeID, CompanyName FROM Account) T01
FULL OUTER JOIN
(SELECT DISTINCT AccountID, TransactionType, MAX(SaleDate) LastSale FROM TransHeader group by AccountID, TransactionType, SaleDate) T02
ON T01.ID = T02.AccountID
WHERE ( ( T01.IsActive = 1 )AND
( (Select Max(SaleDate)From TransHeader Where AccountID = T01.ID AND TransactionType in (1,6) AND SaleDate is NOT NULL)
BETWEEN '01/01/2016' AND '12/31/2018 23:59:00' AND (Select Max(SaleDate)From TransHeader Where AccountID = T01.ID AND TransactionType in (1,6) AND SaleDate is NOT NULL) IS NOT NULL
)
)
ORDER BY T01.CompanyName
I thought the FULL OUTER JOIN was the ticket but it did not work and I am stuck.
Sample data Account Table (T01)
ID CompanyName IsActive ClassTypeID
1 ABC123 1 1
2 CDE456 1 1
3 EFG789 1 1
4 Test123 0 1
5 Test456 1 1
6 Test789 0 1
Sample data Transheader table (T02)
AccountID TransactionType SaleDate
1 1 02/03/2012
2 1 03/04/2013
3 1 04/05/2014
4 1 05/06/2014
5 1 06/07/2014
6 1 07/08/2015
1 1 08/09/2016
1 1 01/15/2016
2 1 03/20/2017
2 1 03/21/2017
3 1 03/04/2017
3 1 04/05/2018
3 1 05/27/2018
4 1 06/01/2018
5 1 07/08/2018
5 1 08/01/2018
5 1 10/11/2018
6 1 11/30/2018
Desired Results
CompanyName LastSale (Notes note returned in the result)
ABC123 01/15/2016 (Max(SaleDate) LastSale for ID=1)
CDE456 03/21/2017 (Max(SaleDate) LastSale for ID=2)
EFG789 05/27/2018 (Max(SaleDate) LastSale for ID=3)
Testing456 10/11/2018 (Max(SaleDate) LastSale for ID=5)
ID=4 & ID=6 are note returned because IsActive = 0 for these records.
One option is to select the maximum date in the select clause.
select
a.*,
(
select max(th.saledate)
from transheader th
where th.accountid = a.id
and th.saledate >= '2016-01-01'
and th.saledate < '2019-01-01'
) as max_date
from account a
where a.isactive = 1
order by a.id;
If you only want to show transaction headers with sales dates in the given date range, then you can just inner join the maximum dates with the accounts. In order to do so, you must group your date aggregation per account:
select a.*, th.max_date
from account a
join
(
select accountid, max(saledate) as max_date
from transheader
and saledate >= '2016-01-01'
and saledate < '2019-01-01'
group by accountid
) th on th.accountid = a.id
where a.isactive = 1
order by a.id;
select CompanyName,MAX(SaleDate) SaleDate from Account a
inner join Transheader b on a.id = b.accountid
group by CompanyName
order by 1

How to Sum previos rows summatory to find a balance in SQL Server

I'm working on a service that needs to calculate how much a customer owes, according to a total invoice value and the partial payments that the customer has made.
So, in a tableA I have a row with the invoice total value:
[dbo].[TableA]
ID CustomerId InvoiceVal
1 12 1000
2 11 2000
3 10 5000
4 14 15000
5 12 100
6 16 8000
7 18 3200
In a TableB I have the record of each customer's partial payments they have made to each invoice:
[dbo].[TableB]
ID InvoiceId Payment
1 1 150
2 3 50
3 1 120
4 1 100
5 5 90
6 4 7500
So, as you can see, the customer 12 has an invoice for $1000 and has made 3 payment that sum $370
I need to be able to se the partial total owed in each row, this is the expected result:
No. InoviceId CustomerId Payment Owed
1 1 12 150 850
2 1 12 120 730
3 1 12 100 630
So far, this is my code:
DECLARE #invid int = '1'
DECLARE #invoicetotal numeric(18,2)
SET #invoicetotal =
(
SELECT
[dbo].[TableA].[InvoiceVal]
FROM [dbo].[TableA]
WHERE
([dbo].[TableA].[ID] = #invid)
)
SELECT
*,
SUM(#invoicetotal - [dbo].[TableB].[Payment]) OVER(ORDER BY [dbo].[TableB].[ID] ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS [Owed]
FROM [dbo].[TableB]
WHERE
([dbo].[TableB].[InvoiceId] = #invid)
But this is what I get:
ID InvoiceId Payment Owed
1 1 150.00 NULL
3 1 120.00 850.00
4 1 100.00 1730.00
I need to sum the previous payment on each row.
Thanks!
Something like this would help
SELECT
ROW_NUMBER() OVER (ORDER BY TableB.ID ASC) NO,
CustomerId,
Payment,
InvoiceVal - SUM(Payment) OVER (PARTITION BY TableA.ID ORDER BY TableB.Id ASC) Owed
FROM TableA
INNER JOIN TableB
ON TableA.Id = TableB.InvoiceId
WHERE
CustomerId = 12
Working Fiddle
Your query will depend on what you are trying to make as your final output.
If you want just ONE CustomerID, go with:
SELECT a.ID AS InvoiceID, a.CustomerID, a.InvoiceVal AS StartInvoice
, b.ID AS bid, b.Payment
, a.InvoiceVal - ISNULL(SUM(b.Payment) OVER (PARTITION BY a.ID ORDER BY b.id),0) AS owed
FROM TableA a
LEFT OUTER JOIN TableB b ON a.ID = b.InvoiceID
WHERE a.CustomerID = 12
And if that CustomerID doesn't have any payments, you want to use a LEFT JOIN so that you don't eliminate an amount owed.
SELECT a.ID AS aid, a.CustomerID, a.InvoiceVal AS StartInvoice
, b.ID AS bid, b.Payment
, a.InvoiceVal - ISNULL(SUM(b.Payment) OVER (PARTITION BY a.ID ORDER BY b.id),0) AS owed
FROM TableA a
LEFT OUTER JOIN TableB b ON a.ID = b.InvoiceID
WHERE a.CustomerID = 11
I also added an ISNULL() around Payment to keep from nulling out your owed amount. It could also be added to the InvoiceVal to account for a CustomerID who hasn't been invoiced yet, if that was needed (or possible from other tables).
IF you want to get ALL CustomerIDs, you'll have to account for that in your partition.
SELECT s1.CustomerID, aid AS InvoiceID, s1.bid, s1.Payment
, (s1.StartInvoice - s1.runningPayment) AS Owed
FROM (
SELECT a.ID AS aid, a.CustomerID, a.InvoiceVal AS StartInvoice
, b.ID AS bid, b.Payment
, ISNULL(SUM(b.Payment) OVER (PARTITION BY a.CustomerID, a.ID ORDER BY b.id),0) AS runningPayment
FROM TableA a
LEFT OUTER JOIN TableB b ON a.ID = b.InvoiceID
) s1
ORDER BY s1.CustomerID, s1.aid, s1.bid
Fiddle demonstrates overpayment or paying total balance for 0 owed.

Select column from one table order by column from another table

I have 2 tables:
Customer_Master:
CUSTOMER_ID CUSTOMER_NAME
----------------------------
1 Test1
2 Test2
Ticket_Master:
TICKETID CUSTOMER_ID UPDATEDATE
--------------------------------------------------
1 1 2017-03-03 00:00:00.000
2 1 2017-03-20 20:09:31.000
3 2 2017-03-20 20:11:00.000
4 2 2017-03-20 20:15:29.000
I need results with all elements from Table 1 order by updatetime of Ticket_Mater. Join results in Duplicate rows whereas I need distinct rows from Customer_Master.
SELECT
a.CUSTOMER_ID, MAX(b.UPDATEDATE)
FROM
customer_master AS a
INNER JOIN
Ticket_master AS b ON a.CUSTOMER_ID = b.CUSTOMERID
GROUP BY
a.CUSTOMER_ID
ORDER BY
MAX(b.UPDATEDATE) DESC
Above query returns order by update but not all elements of customer_master.
SELECT
a.*, b.UPDATEDATE
FROM
customer_master AS a
INNER JOIN
Ticket_master AS b ON a.customer_id = b.customerid
ORDER BY
b.UPDATEDATE desc
This Query Return Duplicate rows from Ticket_master.
Please help. Every Help will be highly Appreciated.
As there can be several entries in Ticket_Master for a CUSTOMER_ID you must decide by which of the possible dates to sort. This would usually be the first or the latest date per CUSTOMER_ID.
You can do this with a subquery:
select *
from customer_master cm
order by
(
select max(tm.updatedate)
from ticket_master tm
where tm.customer_id = cm.customer_id
) desc;
Try this
SELECT distinct(a.*)
FROM customer_master AS a inner join
Ticket_master AS b ON a.CUSTOMER_ID = b.CUSTOMERID
order by b.UPDATEDATE desc

sql group by and max and other values

i have a table that contains:
itemid inventdimid datephysical transrefid
10001 123 2015-01-02 300002
10002 123 2015-01-03 3566
10001 123 2015-02-05 55555
10002 124 2015-02-01 4545
The result i want
itemid inventdimid datephysical transrefid
10001 123 2015-02-05 555
10002 123 2015-01-03 3566
10002 124 2015-02-01 4545
MY query:
SELECT a.itemid,a.inventdimid,max(a.datephysical),a.transrefid
FROM a where dataareaid = 'ermi'
group by a.itemid,a.inventdimid
it is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Use the ANSI standard row_number() function:
select t.*
from (select t.*,
row_number() over (partition by itemid, inventdimid
order by datephysical desc) as seqnum
from table t
) t
where seqnum = 1;
Find max(a.datephysical) for each itemid, inventdimid combination, select all rows from that date.
SELECT itemid, inventdimid, datephysical, transrefid
FROM a a1
where dataareaid = 'ermi'
and datephysical = (select max(datephysical)
from a a2
where a1.itemid = a2.itemid
and a1.inventdimid = a2.inventdimid
and a2.dataareaid = 'ermi')
You have to create a temporary table with your GROUP BY and then join the original table with it.
Try this:
SELECT T1.*,T2.datephysical,T2.transrefid FROM
(SELECT itemid,inventdimid
FROM TableName
GROUP BY itemid,inventdimid) T1 JOIN
(SELECT itemid,inventdimid,datephysical,transrefid
FROM TableName) T2 ON T1.itemid=T2.itemid AND T1.inventdimid=T2.inventdimid
I'm assuming you want the transrefid corresponding with the a.datephysical shown? This would be done by turning the column into a subquery:
SELECT a.itemid,a.inventdimid,max(a.datephysical),
(SELECT b.transrefid FROM MY_TABLE b where
b.datareaid = 'ermi' and b.itemid = a.itemid and b.inventdimid = a.itemid
and b.datephysical = max(a.datephysical)) as transrefid
FROM MY_TABLE a where dataareaid = 'ermi'
group by a.itemid, a.inventdimid
Some databases may not support this syntax though and it will fail if there are more than one records with the same date.