Subtracting two columns within the sql query - sql-server

I have been trying to subtract two columns in sql server to form a third one. Below is my query
select AD.Id, Sum(APS.Amount) AS TotalDue,
isnull((select sum(Amount) from Activation where InvoiceId in (select InvoiceId from Invoices where AgreementId = AD.Id)),0)
As AllocatedToDate
from AdvantageDetails AD
inner join AllPaymentsSubstantial APS
on APS.AgreementId=AD.Id
where AD.OrganizationId=30
group by AD.Id
What I tried is below but it is not working. :
select AD.Id, Sum(APS.Amount) AS TotalDue,
isnull((select sum(Amount) from Activation where InvoiceId in (select InvoiceId from Invoices where AgreementId = AD.Id)),0)
As AllocatedToDate , (TotalDue-AllocatedToDate) as NewColumn
from AdvantageDetails AD
inner join AllPaymentsSubstantial APS
on APS.AgreementId=AD.Id
where AD.OrganizationId=30
group by AD.Id
At last I tried it using a CTE which worked fine. But I want to do it without creating CTE. Can there be any other way for performing the same functionality. I do not want to use CTE because it is forcasted that there
can be other columns which will be calculated in future.
with CTE as(select AD.Id, Sum(APS.Amount) AS TotalDue,
isnull((select sum(Amount) from Activation where InvoiceId in (select InvoiceId from Invoices where AgreementId = AD.Id)),0)
As AllocatedToDate , (TotalDue-AllocatedToDate) as NewColumn
from AdvantageDetails AD
inner join AllPaymentsSubstantial APS
on APS.AgreementId=AD.Id
where AD.OrganizationId=30
group by AD.Id) select * , (CTE.TotalDue-CTE.AllocatedToDate)As Newcolumn from CTE

You can do it without a CTE by repeating the entire formula that makes up AllocatedToDate.
You cannot use the alias of a column in the SELECT list, so you cannot do this:
SELECT {some calculation} AS ColumnA, (ColumnA - ColumnB) AS ColumnC
If you don't want to use a CTE or derived table, you have to do this:
SELECT {some calculation} AS ColumnA, ({some calculation} - ColumnB) AS ColumnC
And by the way, I can't imagine why the possibility of future columns being added is a reason not to use a CTE. To me, it sounds like a reason TO use a CTE, as you will only have to make changes in one place in the code, and not duplicate the same code in different places in the same query.

You can just use nested queries:
select Id, TotalDue, AllocatedToDate, (TotalDue-AllocatedToDate) as NewColumn
from (
select AD.Id, Sum(APS.Amount) AS TotalDue,
isnull((select sum(Amount) from Activation where InvoiceId in (select InvoiceId from Invoices where AgreementId = AD.Id)),0)
As AllocatedToDate
from AdvantageDetails AD
inner join AllPaymentsSubstantial APS
on APS.AgreementId=AD.Id
where AD.OrganizationId=30
group by AD.Id
) x

Related

Create a temporary table showing the most eventful country for each year in SQL Server

I have an exercise in SQL Server: I have two tables Country and Events.
The Events table holds the event details including the city where an event happens. The table Events has a foreign key CountryID (CountryID is the primary key in table Country).
I need to create a temporary table showing the most eventful country for each year.
Any help would be appreciated
Thanks
You weren't far off with your attempt, but you need to use a CTE to aggregate your data first. I've assumed that the final order of your data is important, so I used a second CTE, rather than a TOP 1 WITH TIES tio get the final result:
WITH CTE AS(
SELECT YEAR(e.EventDate) AS YearOfEvent,
c.CountryName,
COUNT(e.CountryID) AS NumberOfEvents
FROM [dbo].[tblEvent] AS e
INNER JOIN tblCountry AS c ON e.CountryID = c.CountryID
GROUP BY e.CountryId,
c.CountryName,
YEAR(e.EventDate)),
RNs AS(
SELECT YearOfEvent,
CountryName,
NumberOfEvents,
ROW_NUMBER() OVER (PARTITION BY YearOfEvent ORDER BY CTE.NumberOfEvents DESC) AS RN
FROM CTE)
SELECT YearOfEvent,
CountryName,
NumberOfEvents
FROM RNs
WHERE RN = 1
ORDER BY RNs.YearOfEvent ASC;

sql auto increment based on ID

Im using SQLServer 2017, I have a table that I want to auto increment for each id.
Example Table A has columns
PolicyID, ClaimID, TranId
with the following values
ABC123, 111, 1
when another row gets inserted/added TranId will show 2 and so on but if the PolicyID is different lets say ABC456 then the expected TranId should be 1 but my table just keeps incrementing instead of per PolicyID.
You are trying to create a sequence and this shouldn't be stored in the table.
Try creating a view:
create vw_xxx as
(
select PolicyID, ClaimID
, TranId = row_number() over (partition by PolicyID order by ClaimID)
from tableXXX
)
This is an example of how to do this. You need to partition and order by properly to get the right sequence.
If this table is large then you want to have an index on the partition,ordered by columns.
Everytime you enter a new row in your MyTable you have to run the following UPDATE:
UPDATE Table_A
SET Table_A.TranId = Table_B.[TranId]
FROM MyTable AS Table_A
INNER JOIN (
SELECT PolicyID, ClaimID, ROW_NUMBER() OVER (PARTITION BY PolicyID, ClaimID ORDER BY ClaimID) AS [TranId]
FROM MyTable
) Table_B
ON Table_A.PolicyID = Table_B.PolicyID AND Table_A.ClaimID = Table_B.ClaimID

SQL Server - Distinct case in one column but last record in second column

I have a requirement (simple, but can't find simple solution) to fetch mobile number and unique transaction id (latest transaction would be good, but any transaction id is also ok)
Sample Data
Seq. Mobile No. Transaction No.
1 1234567890 ABC1234
2 2345678901 ABC2392
3 2345678901 ABC2782
I simply want to find mobile number 2345678901 and any of the one transaction, however latest would be good.
Output
Seq. Mobile No. Transaction No.
1 1234567890 ABC1234
2 2345678901 ABC2782
I know simply DISTINCT won't work, so not sure what's the best way to get the outcome.
I found a way to do it via sub-query, but I want to do it in single query for better performance.
Plz Help!!
You can use ROW_NUMBER for this:
SELECT Seq, MobileNo, TransactionNo
FROM (
SELECT Seq, MobileNo, TransactionNo,
ROW_NUMBER() OVER (PARTITION BY MobileNo ORDER BY Seq DESC) AS rn
FROM mytable) AS t
WHERE t.rn = 1
The above query will pick exactly one record per MobileNo: the one having the greatest Seq value.
You can use group by.
select [Seq], [Mobile No], [Transaction No] from yourtable t1
inner join
(select [Mobile No], max([Transaction No]) as T_no from yourtable
group by [Mobile No]) t2
on t1.[Mobile No]=t2.[Mobile No] and t1.[Transaction No]=t2.T_no
Right query gives you latest [Transaction No] per [Mobile No] and left query is used only for finding matching [Seq].
CREATE TABLE #Transaction
(
Seq VARCHAR(12),
MobileNo VARCHAR(12),
TransactionNo VARCHAR(12)
)
INSERT INTO #Transaction VALUES
(1,'1234567890','ABC1234')
,(2,'2345678901','ABC2392')
,(3,'2345678901','ABC2782')
SELECT DT.Seq,DT.MobileNo,DT.TransactionNo FROM
(SELECT Seq,
MobileNo,
TransactionNo,
ROW_NUMBER() OVER(PARTITION BY MobileNo ORDER BY Seq) AS Rn
FROM
#Transaction) DT
WHERE DT.Rn = 1

Select a random database row based on another query

For internal control we would like to select a single random invoice for each of multiple invoice types and regions.
Here's the SQL to get a set of distinct Invoice Types and Regions
select InvoiceType,RegionID
from Invoices
group by InvoiceType, RegionID
For each row this returns I need to fetch a random row with that InvoiceType and RegionID. This is how I'm fetching random rows:
SELECT top 1
CustomerID
,InvoiceNum
,Name
FROM Invoices
JOIN Customers on Customers.CustomerID=Invoices.CustomerID
where InvoiceType=X and RegionID=Y
ORDER BY NEWID
But I don't know how to run this select statement foreach() row the first statement returns. I could do it programmatically but I would prefer an option using only a stored procedure as this query isn't supposed to need a program.
WITH cteInvoices AS (
SELECT CustomerID, InvoiceNum, Name,
ROW_NUMBER() OVER(PARTITION BY InvoiceType, RegionID ORDER BY NEWID()) AS RowNum
FROM Invoices
)
SELECT c.CustomerID, c.InvoiceNum, c.Name
FROM cteInvoices c
WHERE c.RowNum = 1;

How do I join the first row of a subquery?

I've got a table of invoices and a child table of related data related by key. In particular, for each invoice, I'm interested in only the first related row from the child table. Given that I want the one related row for every invoice key - how do I accomplish this?
Select i.[Invoice Number],
c.[Carrier Name]
From Invoice i
Left Join Carriers c on i.[InvoiceKey] = c.[InvoiceKey]
Where -- what?
I guess semantically speaking, what I'm looking for something akin to the concept of Top 1 c.CarrierName Group by InvoiceKey (or what would be the concept of that if that were possible in T-SQL.)
I've thought about doing a left join on a subquery, but that doesn't seem very efficient. Does anyone have any T-SQL tricks to achieve this efficiently?
Edit: Sorry guys, I forgot to mention this is SQL Server 2000, so while I'm going to give upvotes for the current SQL Server 2005/2008 responses that will work, I can't accept them I'm afraid.
Provided that Carriers has a PRIMARY KEY called id:
SELECT i.[Invoice Number],
c.[Carrier Name]
FROM Invoice i
JOIN Carriers c
ON c.id =
(
SELECT TOP 1 ID
FROM Carriers ci
WHERE ci.InvoiceKey = i.InvoiceKey
ORDER BY
id -- or whatever
)
;with cteRowNumber as (
select c.InvoiceKey, c.[Carrier Name], ROW_NUMBER() over (partition by c.InvoiceKey order by c.[Carrier Name]) as RowNum
from Carriers c
)
select i.[Invoice Number],
rn.[Carrier Name]
from Invoice i
left join cteRowNumber rn
on i.InvoiceKey = rn.InvoiceKey
and rn.RowNum = 1
This works for me:
select ir.[Invoice Number], c.[Carrier Name]
from
(select ROW_NUMBER() over (order by i.[Invoice Number] asc) AS RowNumber, i.[Invoice Number], i.InvoiceKey
from Invoice i) AS ir
left join Carriers c
on ir.InvoiceKey = c.InvoiceKey
where RowNumber = 1
union all
select ir.[Invoice Number], NULL as [Carrier Name]
from
(select ROW_NUMBER() over (order by i.[Invoice Number] asc) AS RowNumber, i.[Invoice Number]
from Invoice i) AS ir
where RowNumber > 1
or
select TOP 1 i.[Invoice Number], c.[Carrier Name]
from Invoice i
left join Carriers c
on i.InvoiceKey = c.InvoiceKey
union all
select ir.[Invoice Number], NULL as [Carrier Name]
from
(select ROW_NUMBER() over (order by i.[Invoice Number] asc) AS RowNumber, i.[Invoice Number]
from Invoice i) AS ir
where RowNumber > 1
This is how I would do it, using a slightly different syntax than yours (MySQL style), but I guess you could apply it to your solution as well:
SELECT i.invoiceNumber, c.carrierName
FROM Invoice as i
LEFT JOIN Carriers as c ON (c.id = (SELECT id FROM Carriers WHERE invoiceKey = i.invoiceKey ORDER BY id LIMIT 1))
This will take all records from Invoice, and join it with one (or zero) record from Carriers, specifically the record which has the same invoiceKey and only the first one.
As long as you have an index on Carriers.invoiceKey the performance of this query should be acceptable.
Sebastian
Alternatively you could use OUTER APPLY as well.
Please notice the use of angle brackets for unknown field names:
Select i.[Invoice Number], c.[Carrier Name], x.<Carrier_field1>
From Invoice i
OUTER APPLY
(
SELECT TOP 1
FROM Carriers c
WHERE c.[InvoiceKey] = i.[InvoiceKey]
ORDER BY <order _clause>
) x
In such cases I often employ a device which I here apply to your example and describe below:
SELECT
i.[Invoice Number],
c.[Carrier Name]
FROM Invoice i
INNER JOIN Carriers c ON i.InvoiceKey = c.InvoiceKey
INNER JOIN (
SELECT MIN(ID) AS ID
FROM Carriers
GROUP BY InvoiceKey
) c_top ON c.ID = c_top.ID
I think, this is roughly what Quassnoi has posted, only I try to avoid using SELECT TOPs like that.
Invoice is joined with Carriers based on their linking expression (InvoiceKey in this case). Now, Carriers can have multiple rows for the same InvoiceKey, so we need to limit the output. And that is done using a derived table.
The derived table groups rows from Carrier based on the same expression that is used for linking the two tables (InvoiceKey).
And there's another way: instead of joining the derived table you could use IN (subquery) with the same effect. That is, the complete query would then look like this:
SELECT
i.[Invoice Number],
c.[Carrier Name]
FROM Invoice i
INNER JOIN Carriers c ON i.InvoiceKey = c.InvoiceKey
AND c.ID IN (SELECT MIN(ID) FROM Carriers GROUP BY InvoiceKey)
group by carriername having max(invoicenumber)
to get the first carrier for each invoice:
group by invoicenumber having max(carriername)
-- substitute the column you want to order by for carrier name to change which is 'first'

Resources