Need Solution for SQL query - sql

My SQL query is ,
SELECT
T.TAX_NAME,A.TAX_AMT_ID,A.TAX_MAP_ID,A.EFFECTIVE_FROM
FROM
MAS_TAX T
INNER JOIN
MAS_TAX_MAP M ON T.TAX_ID = M.TAX_ID
LEFT OUTER JOIN
MAS_TAX_AMOUNT A ON M.TAX_MAP_ID = A.TAX_MAP_ID
WHERE
EFFECTIVE_FROM <= GETDATE()
I am getting output for the above query is:
TAX_NAME TAX_AMT_ID TAX_MAP_ID EFFECTIVE_FROM
-------------------------------------------------------
Income Tax 12 5 02-06-2014
Service Tax 16 4 02-06-2014
Gift Tax 3 1 29-05-2014
Gift Tax 2 1 28-05-2014
Gift Tax 4 1 27-05-2014
But I need to get below output. Can any one help me?
TAX_NAME TAX_AMT_ID TAX_MAP_ID EFFECTIVE_FROM
-------------------------------------------------------
Income Tax 12 5 02-06-2014
Service Tax 16 4 02-06-2014
Gift Tax 3 1 29-05-2014

You seem to want the most recent record. You can do this in SQL Server using row_number():
select TAX_NAME, TAX_AMT_ID, TAX_MAP_ID, EFFECTIVE_FROM
from (select t.*,
row_number() over (partition by tax_name order by effective_from desc) as seqnum
from table t
) t
where seqnum = 1;
EDIT:
For your particular query:
SELECT T.TAX_NAME, A.TAX_AMT_ID, A.TAX_MAP_ID, A.EFFECTIVE_FROM
FROM (SELECT T.TAX_NAME, A.TAX_AMT_ID, A.TAX_MAP_ID, A.EFFECTIVE_FROM,
ROW_NUMBER() OVER (PARTITION BY T.TAX_NAME ORDER BY A.EFFECTIVE_FROM DESC) as seqnum
FROM MAS_TAX T INNER JOIN
MAS_TAX_MAP M
ON T.TAX_ID = M.TAX_ID LEFT OUTER JOIN
MAS_TAX_AMOUNT A
ON M.TAX_MAP_ID = A.TAX_MAP_ID
WHERE EFFECTIVE_FROM <= GETDATE()
) t
WHERE seqnum = 1;

Related

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

Return last amount for each element with same ref_id

I have 2 tables, one is credit and other one is creditdetails.
Creditdetails creates new row every day for each of credit.
ID Amount ref_id date
1 2 1 16.03
2 3 1 17.03
3 4 1 18.03
4 1 2 16.03
5 2 2 17.03
6 0 2 18.03
I want to sum up amount of every row with the unique id and last date. So the output should be 4 + 0.
You can use ROW_NUMBER to filter on the latest amount per ref_id.
Then SUM it.
SELECT SUM(q.Amount) AS TotalLatestAmount
FROM
(
SELECT
cd.ref_id,
cd.Amount,
ROW_NUMBER() OVER (PARTITION BY cd.ref_id ORDER BY cd.date DESC) AS rn
FROM Creditdetails cd
) q
WHERE q.rn = 1;
A test on db<>fiddle here
With this query:
select ref_id, max(date) maxdate
from creditdetails
group by ref_id
you get all the last dates for each ref_id, so you can join it to the table creditdetails and sum over amount:
select sum(amount) total
from creditdetails c inner join (
select ref_id, max(date) maxdate
from creditdetails
group by ref_id
) g
on g.ref_id = c.ref_id and g.maxdate = c.date
I think you want something like this,
select sum(amount)
from table
where date = ( select max(date) from table);
with the understanding that your date column doesn't appear to be in a standard format so I can't tell if it needs to be formatted in the query to work properly.

Selecting rows with highest date

I have the following query that throws a result like in the example:
SELECT P.IdArt, P.IdAdr, P.gDate, P.Price
FROM dbo.T_PriceData AS P INNER JOIN
dbo.T_Adr AS A ON P.IdAdr = A.IdAdr INNER JOIN
dbo.T_Stat AS S ON A.IdStat = S.IdStat
GROUP BY P.IdArt, P.IdAdr, P.gDate, P.Price
IdArt IdAdr gDate Price
1 10 01/01/2018 1.25
1 10 02/01/2018 1.17
1 10 03/01/2018 1.18
2 15 01/01/2018 1.03
2 18 10/01/2018 0.12
3 25 12/01/2018 0.98
3 25 28/01/2018 1.99
4 30 15/01/2018 2.55
5 35 08/01/2018 0.11
The final result I want is:
When the IdArt and IdAdr are the same, there should be only one row with the highest date of all rows (CASE IdArt 1)
When IdArt is the same but IdAdr is different, there should be a row with each IdAdr with the highest date for each IdAdr. (CASE IdArt 2)
Price doens't affect anything.
So the final table I would like to have is:
IdArt IdAdr gDate Price
1 10 03/01/2018 1.18
2 15 01/01/2018 1.03
2 18 10/01/2018 0.12
3 25 28/01/2018 1.99
4 30 15/01/2018 2.55
5 35 08/01/2018 0.11
How can I do that?
I tried with a having clausule selecting by MAX(gDate) but, of course, I only get one row with the max date from the whole database.
There are lots of answers out there on how to do this, however, this gets you what you are after:
SELECT TOP 1 WITH TIES
P.IdArt,
P.IdAdr,
P.gDate,
P.Price
FROM dbo.T_PriceData P
--INNER JOIN dbo.T_Adr A ON P.IdAdr = A.IdAdr --You don't reference this in the SELECT or WHERE. Why is it here?
--INNER JOIN dbo.T_Stat S ON A.IdStat = S.IdStat --You don't reference this in the SELECT or WHERE. Why is it here?
ORDER BY ROW_NUMBER() OVER (PARTITION BY P.IdArt, P.IdAdr ORDER BY P.gDate DESC);
Edit: If the JOINs are there to ensure that there are rows in the other tables, then as per the comments I would use EXISTS. If you just use JOIN, and only returning rows from the first table, then you could end up with duplicate rows.
SELECT TOP 1 WITH TIES
P.IdArt,
P.IdAdr,
P.gDate,
P.Price
FROM dbo.T_PriceData P
WHERE EXISTS (SELECT 1
FROM dbo.T_Adr A
WHERE P.IdAdr = A.IdAdr)
AND EXISTS (SELECT 1
FROM dbo.T_Stat S
WHERE A.IdStat = S.IdStat)
ORDER BY ROW_NUMBER() OVER (PARTITION BY P.IdArt, P.IdAdr ORDER BY P.gDate DESC);
You want the highest date for each IdArt/IdAdr combination. Window functions are tempting, but the most efficient method is often a correlated subquery.
Your query is only selecting from T_PriceData, so the rest of the query (the joins and group by) do not seem necessary -- unless the joins are filtering the data which seems unlikely because the joins are to reference tables.
So I would recommend:
SELECT P.IdArt, P.IdAdr, P.gDate, P.Price
FROM dbo.T_PriceData P
WHERE P.gDate = (SELECT MAX(P2.gDate)
FROM dbo.T_PriceData P2
WHERE P2.IdArt = P.IdArt AND
P2.IAdr = P.IdAdr
);
For performance you want indexes on (IdArt, IdAdr, gDate).
You can use ROW_Number():
SELECT
q.IdArt
, q.IdArt
, q.IdADr
, q.gDate
, q.Price
FROM (
SELECT
t.IdArt
, t.IdADr
, t.gDate
, t.Price
, ROW_NUMBER() OVER (PARTITION BY t.IdArt, t.IdADr ORDER BY t.gDate DESC) rn
FROM dbo.T_PriceData t
) q
WHERE q.rn = 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.

Find nearest SUM amount from table SQL SERVER

Query
Declare #table1 TABLE (accountno varchar(max), saved_amount decimal)
INSERT INTO #table1 VALUES
('001',25),
('002',5)
Declare #table2 TABLE (accountno varchar(max), payamount decimal,ilno int)
INSERT INTO #table2 VALUES
('001',10,1),
('001',10,2),
('001',10,3),
('001',10,4),
('002',10,1),
('002',10,2);
WITH aa
AS (
SELECT a.*
,b.ilno
,b.payamount
,SUM(payamount) OVER (
PARTITION BY a.accountno ORDER BY CAST(a.accountno AS INT)
,ilno
) AS total_amount
FROM #table1 a
LEFT JOIN #table2 b ON a.accountno = b.accountno
)
,bb
AS (
SELECT accountno
,MAX(ilno) AS ilno
FROM aa
WHERE saved_amount >= total_amount
GROUP BY accountno
)
SELECT a.* FROM aa a INNER JOIN bb b on a.accountno =b.accountno AND a.ilno = b.ilno
Result
accountno | saved_amount | ilno | payamount | total_amount
----------------------------------------------------------
001 | 25 | 2 | 10 | 20
Expected Result
accountno | saved_amount | ilno | payamount | total_amount
----------------------------------------------------------
001 | 25 | 2 | 10 | 20
002 | 5 | 1 | 10 | 10
What I want is
If saved_amount is less than the first ilno, then get the first ilno else
get the highest ilno where saved_amount>=total_amount
You have a running total that you compare with the saved amount. You want the highest running total that doesn't exceed the saved amount. But in case even the initial pay amount exceeds the saved amount already, you want to default to this record. So the main task is to find a way of ranking the records. In my query I do it like this:
Prefer records where the running total does not exceed the saved amount.
Then look at the abolute of their difference and take the smallest.
There are certainly other ways that achieve the same. Maybe even methods that you find more readable. Then just adjust the order by clause in the ranking query.
with summed as
(
select
t1.*,
from #table1 t1
join
(
select
ilno,
payamount,
sum(payamount) over (partition by accountno order by ilno) as total_amount
from #table2
) on t2.accountno = t1.accountno
)
, ranked as
(
select summed.*,
row_number() over (partition by accountno
order by case when saved_amount >= total_amount then 1 else 2 end,
abs(saved_amount - total_amount)
) as rn
)
select *
from ranked
where rn = 1;
This is not the "nearest sum", as you said in the title, but the one that obeys the specified rules. So with a saved amount of 100 and paid amounts of first 1 and then 100, you'd get the record with a total of 1 (which is 99 less than the saved amount) and not the one with a total of 101 (which is only 1 more than the saved amount).
Other way to solve using flags:
first calculated one flag to point if saved_amount >= payamount for current row
calculated three more flags:
group_flag to show is there a case where saved_amount >= payamount for the given accountno
[min_ilno] and [max_ilno] for given account
Having this flags, the final result set is calculated easily. Here is the code:
WITH DataSource AS
(
SELECT a.*
,b.ilno
,b.payamount
,SUM(payamount) OVER (PARTITION BY a.accountno ORDER BY ilno) AS total_amount
,IIF(a.saved_amount >= SUM(payamount) OVER (PARTITION BY a.accountno ORDER BY ilno), 1, 0) AS [flag]
FROM #table1 a
LEFT JOIN #table2 b
ON a.accountno = b.accountno
),
DataSourceFinal AS
(
SELECT *
,MAX(flag) OVER (PARTITION BY accountno) as [group_flag]
,MIN(IIF(flag = 0 ,ilno, NULL)) OVER (PARTITION BY accountno) as [min_ilno]
,MAX(IIF(flag = 1 ,ilno, NULL)) OVER (PARTITION BY accountno) as [max_ilno]
FROM DataSource
)
SELECT accountno, saved_amount, ilno, payamount, total_amount
FROM DataSourceFinal
WHERE ([group_flag] = 1 AND [ilno] = [max_ilno])
OR ([group_flag] = 0 AND [ilno] = [min_ilno]);
and the output: