Adding multiple results form multiple subqueries - sql

I am running a SQL Server query to get the sum of the transaction amount and the pending amount and the extra amount. I found that the best solution to do that is using subqueries, hence I am using subqueries for the same, which is the fastest way. However, although I can extract the amount from each subquery, I can't figure out how to add them together so I can sort by the total number.
Here's the current query:
SELECT
*, trans + pend + extra AS totalamount
FROM
(SELECT
(SELECT trnammt
FROM Tbl_Emi
WHERE Status IN ('N', 'P')
AND lastrecdate = (CAST(GETDATE() AS DATE))
AND CardNo = CardNo) AS TRANS,
(SELECT pendamt
FROM Tbl_Emi
WHERE Status IN ('N', 'P')
AND lastrecdate = (CAST(GETDATE() AS DATE))
AND CardNo = CardNo) AS PEND,
(SELECT extraamt
FROM Tbl_Emi
WHERE Status IN ('N', 'P')
AND lastrecdate = (SELECT MIN(lastrecdate) FROM Tbl_Emi)
AND CardNo = CardNo) AS EXTRA
FROM
Tbl_Emi) q
I need to add together trans and pend and extra to get 'totalamount'. SQL Server won't allow you to use simple syntax to do calculations on aliases, but I presume there's another way to do this?

Here are three problems I can readily see:
The subqueries are not aggregated, so if they return multiple rows, you will get an error.
The correlation conditions only refer to the subquery (so they are not really correlated).
The results could be NULL, so the sum would be NULL.
This version fixes these three problems:
SELECT e.*,
(COALESCE(trans, 0) + COALESCE(pend, 0) + COALESCE(extra, 0)) AS totalamount
FROM (
SELECT (
SELECT SUM(trnammt)
FROM Tbl_Emi e2
WHERE Status IN ('N','P')
AND lastrecdate = (CAST(GETDATE() AS DATE))
AND e2.CardNo = e.CardNo
) AS trans,
(
SELECT sum(pendamt)
FROM Tbl_Emi e2
WHERE Status IN ('N','P')
AND lastrecdate = (CAST(GETDATE() AS DATE))
AND e2.CardNo = e.CardNo
) AS pend,
(
SELECT sum(extraamt)
FROM Tbl_Emi e2
WHERE Status IN ('N','P')
AND lastrecdate = (SELECT MIN(lastrecdate) FROM Tbl_Emi)
AND e2.CardNo = e.CardNo
) AS extra
FROM Tbl_Emi e
) e

If you just want sum of trans + pend + extra you need to change the criteria part
SELECT CASE WHEN (lastrecdate = (CAST(GETDATE() AS DATE))
AND CardNo = CardNo) THEN trnammt
ELSE 0
END +
CASE WHEN (pendamt = (CAST(GETDATE() AS DATE))
AND CardNo = CardNo) THEN trnammt
ELSE 0
END +
CASE WHEN (lastrecdate = (SELECT MIN(lastrecdate) FROM Tbl_Emi)
AND CardNo = CardNo) THEN trnammt
ELSE 0
END AS totalamount
WHERE Status IN ('N','P')

Related

Combine two SQL queries into one

SELECT
party.name, leger.partyId, leger.descriptions, leger.cashBySum,
leger.cashByName, leger.date
FROM
leger
INNER JOIN
party ON leger.partyId = party.id
WHERE
(leger.partyId = #partyID)
AND (CAST(leger.date AS date) BETWEEN #fromdate AND #todate)
ORDER BY
leger.date
CROSS JOIN
SELECT
(CASE WHEN (SUM(cashBySum) > SUM(cashByName)) THEN N'جمع'
WHEN (SUM(cashBySum) < SUM(cashByName)) THEN N' بنام'
ELSE '' END) AS balancetype,
SUM(cashBySum) - SUM(cashByName) AS balance,
ABS(SUM(cashBySum) - SUM(cashByName)) AS blc
FROM leger
WHERE (partyId = #partyTypes) AND (date < #fromdate)
How can I combine this query to a single query and use in an RDLC report?
I want to get the last balance and then calculate the balance on date by subtracting the last balance to make the report.
You can combine the queries horizontally with a cross join. Perhaps this is what you mean?
WITH q1 as (
SELECT
party.name, leger.partyId, leger.descriptions,
leger.cashBySum, leger.cashByName, leger.date
FROM
leger INNER JOIN party ON leger.partyId = party.id
WHERE
leger.partyId = #partyID AND CAST(leger.date AS date) BETWEEN #fromdate AND #todate
), q2 as (
SELECT
CASE
WHEN SUM(cashBySum) > SUM(cashByName) THEN N'جمع'
WHEN SUM(cashBySum) < SUM(cashByName) THEN N' بنام'
ELSE ''
END AS balancetype,
SUM(cashBySum) - SUM(cashByName) AS balance,
ABS(SUM(cashBySum) - SUM(cashByName)) AS blc
FROM leger
WHERE partyId = #partyTypes AND date < #fromdate
)
SELECT q1.*, q2.*
FROM q1 CROSS JOIN q2
ORDER BY leger.date
Using an ORDER BY suggests that you have multiple rows in at least the first query. The second is aggregated into a single group so it should be a single row as I'm anticipating; however, if the second somehow has more than one row also then in that case I'm not sure that a cross join is really what you want.
A union would require you to match up the number and types on the columns between the two queries. That's probably why it wasn't working for you.
SELECT
party.name, leger.partyId, leger.descriptions, leger.cashBySum,
leger.cashByName, leger.date
FROM
leger
INNER JOIN
party ON leger.partyId = party.id
AND leger.partyId = #partyID
AND (CAST(leger.date AS date) BETWEEN #fromdate AND #todate)
CROSS JOIN
( SELECT
(CASE WHEN (SUM(cashBySum) > SUM(cashByName)) THEN N'جمع'
WHEN (SUM(cashBySum) < SUM(cashByName)) THEN N' بنام'
ELSE '' END) AS balancetype,
SUM(cashBySum) - SUM(cashByName) AS balance,
ABS(SUM(cashBySum) - SUM(cashByName)) AS blc
FROM leger
WHERE (partyId = #partyTypes) AND (date < #fromdate)
)
ORDER BY
leger.date

TSQL how to use if else in Where clause

I want to create a report, the report will have parameter for the user to select
-IsApprovedDate
-IsCatcheDate
I would like to know how to used the if else in the where clause.
Example if the user selects IsApprovedDate the report will lookup based on approved Date else will lookup based on catch date. In my query I will get top10 fish size base on award order weight here is my query.
;WITH CTE AS
(
select Rank() OVER (PARTITION BY c.trophyCatchCertificateTypeId order by c.catchWeight desc ) as rnk
,c.id,c.customerId, Cust.firstName + ' '+Cust.lastName as CustomerName
,CAST(CONVERT(varchar(10),catchWeightPoundsComponent)+'.'+CONVERT(varchar(10),catchWeightOuncesComponent) as numeric(6,2) ) WLBS
,c.catchGirth,c.catchLength,ct.description county
,t.description award--
,c.trophyCatchCertificateTypeId
,s.specificSpecies--
,c.speciesId
from Catches c
INNER JOIN TrophyCatchCertificateTypes t on c.trophyCatchCertificateTypeId = t.id
INNER JOIN Species s on c.speciesId = s.id
INNER JOIN Counties ct on c.countyId = ct.id
INNER JOIN Customers Cust on c.customerId = cust.id
Where c.bigCatchCertificateTypeId is not null
and c.catchStatusId =1
and c.speciesId =1 and c.isTrophyCatch =1
and c.catchDate >= #startDay and c.catchDate<=#endDay
)
Select * from CTE c1
Where rnk <=10
Just use conditional logic for this:
where . . . and
((#IsApprovedDate = 1 and c.ApprovedDate >= #startDay and c.ApprovedDate <= #endDay) or
(#IsCatchDate = 1 and c.catchDate >= #startDay and c.catchDate <= #endDay)
)
EDIT:
I would actually write this as:
where . . . and
((#IsApprovedDate = 1 and c.ApprovedDate >= #startDay and c.ApprovedDate < dateadd(day, 1 #endDay) or
(#IsCatchDate = 1 and c.catchDate >= #startDay and c.catchDate < dateadd(day, 1, #endDay))
)
This is a safer construct because it work when the date values have times and when they do not.
Performance will be much better if you build the WHERE clause dynamically in your code and then execute it.

SQL Show All Rows

I am getting 331 result rows when I want to get all 595 from the inner most query. The reason it eliminates 264 rows (595-331=264) is that those 264 rows do not meet all of the crieria in STEP #2. The 331 rows that do pass the criteria get a '>>>' in the OK column. So, I want to show the most recent date for the 331 rows, plus I want to show the 'cid' and NULL values for the other 264 that do not pass the criteria in STEP #2.
As a C# programmer, I can think of many ways to do this. But, what is the best way to do this in SQL?
/* STEP #4: ORDER RESULTS*/ /* SEE LINE 43 FOR ALL EVENTS */
SELECT cid
, edate, OK
, (SELECT CASE WHEN OK = '>>>'
THEN DATEDIFF(day, edate, ChartResp.TxPlanDueDate(t2.cid))
ELSE NULL
END
) AS 'DaysBtwnDueDateAndLDOSPrimClin'
, eser, eatt, erecip, Age, ccm, estaff
FROM (
/* STEP #3: SELECT MOST RECENT EVENT FROM STEP 2 FOR EACH CLIENT*/
SELECT *
FROM (
/* STEP #2: SELECT EVENTS THAT PASS FILTER CRITERIA FOR THOSE CLIENTS*/
SELECT --cid, edate, eser, eatt, erecip, DATEDIFF (Year, cbd, GetDate()) AS 'Age', ccm, estaff,
(SELECT CASE WHEN
(eatt IN (1,2)
AND edate > DATEADD(month, -6, getdate())
AND eser NOT IN (100,115,142)
AND erecip NOT IN ('2','7')
AND (( (erecip = '3') AND (DATEDIFF(Year, cbd, GetDate())<10) ) OR (erecip <> '3') )
AND ccm = estaff)
THEN '>>>'
ELSE ''
END
) AS 'OK'
,cid, edate, eser, eatt, erecip, DATEDIFF(Year, cbd, GetDate()) AS 'Age', ccm, estaff
,rownumber() OVER (PARTITION BY cid ORDER BY edate DESC) rn
FROM events INNER JOIN client ON ecaseno = cid
LEFT OUTER JOIN doc ON doc.docdbid = client.cid
WHERE client.cid IN (
/* STEP #1: SELECT CLIENTS THAT ARE IN ORIGINAL OVERDUE TX PLAN REPORT */ SELECT client.cid
FROM client LEFT OUTER JOIN admission ON client.cid = admission.cid
WHERE ((client.ctype = 'AC') AND (admission.alapdt IS NULL))
GROUP BY client.cid
HAVING ((ChartResp.TxPlanDueDate(client.cid) < DATEADD(day, - 1, GETDATE()))
AND (dbo.FFT(client.cid) IS NULL)
AND (dbo.IsHousingOnly(client.cid) IS NULL)
AND (DATEDIFF(day, ChartResp.TxPlanDueDate(client.cid),DATEADD(day, - 1, GETDATE())) > 0))
/* STEP #1 END */
)
AND eser BETWEEN 11 AND 1000
AND ccm = estaff
AND eatt IN (1,2)
AND edate > DATEADD(month, -6, getdate())
AND eser NOT IN (100,115,142)
AND erecip NOT IN ('2','7')
AND (( (erecip = '3') AND (DATEDIFF(Year, cbd, GetDate())<10) ) OR (erecip <> '3') )
GROUP BY cid, edate, eser, eatt, erecip, cbd, ccm, estaff
/* STEP #2 END */
) t1
WHERE rn = 1-- COMMENT THIS OUT TO SEE ALL EVENTS
/* STEP #3 END */
) t2
ORDER BY cid, edate DESC
You can also use a CTE to prepare your required data set and then use a query to join back to your CTE to get the required outpu.

Using SELECT result in another SELECT

So here is my query
SELECT
*
FROM
Score AS NewScores
WHERE
InsertedDate >= DATEADD(mm, -3, GETDATE());
SELECT
ROW_NUMBER() OVER( ORDER BY NETT) AS Rank,
Name,
FlagImg,
Nett,
Rounds
FROM (
SELECT
Members.FirstName + ' ' + Members.LastName AS Name,
CASE
WHEN MenuCountry.ImgURL IS NULL THEN
'~/images/flags/ismygolf.png'
ELSE
MenuCountry.ImgURL
END AS FlagImg,
AVG(CAST(NewScores.NetScore AS DECIMAL(18, 4))) AS Nett,
COUNT(Score.ScoreID) AS Rounds
FROM
Members
INNER JOIN
Score
ON Members.MemberID = Score.MemberID
LEFT OUTER JOIN MenuCountry
ON Members.Country = MenuCountry.ID
WHERE
Members.Status = 1
GROUP BY
Members.FirstName + ' ' + Members.LastName,
MenuCountry.ImgURL
) AS Dertbl
ORDER BY;
The query is to give a result set for a GridView based leaderboard and what I want is to only get the average of Scores that are less than 3 months old. I have this in 2 parts as you can see and obviously it gives an error like this.
Msg 4104, Level 16, State 1, Line 2
The multi-part identifier "NewScores.NetScore" could not be bound.
Which is because of this AVG(CAST(NewScores.NetScore AS DECIMAL(18, 4))) AS Nett
How do I make it so that I can use NewScores there so I'm only getting the average of the scores less than 3 months old?
EDIT: Using the answers people provided I've solved it by using a join in the correct place and here is the correct query:
SELECT ROW_NUMBER() OVER(ORDER BY NETT) AS Rank, Name, FlagImg, Nett, Rounds FROM (SELECT Members.FirstName + ' ' + Members.LastName AS Name, CASE WHEN MenuCountry.ImgURL IS NULL THEN '~/images/flags/ismygolf.png' ELSE MenuCountry.ImgURL END AS FlagImg, AVG(CAST(NewScores.NetScore AS DECIMAL(18, 4))) AS Nett, COUNT(NewScores.ScoreID) AS Rounds FROM Members INNER JOIN (SELECT * FROM Score WHERE InsertedDate >= DATEADD(mm, -5, GETDATE())) NewScores ON Members.MemberID = NewScores.MemberID LEFT OUTER JOIN MenuCountry ON Members.Country = MenuCountry.ID WHERE Members.Status = 1 GROUP BY Members.FirstName + ' ' + Members.LastName, MenuCountry.ImgURL) AS Dertbl ORDER BY Nett ASC
NewScores is an alias to Scores table - it looks like you can combine the queries as follows:
SELECT
ROW_NUMBER() OVER( ORDER BY NETT) AS Rank,
Name,
FlagImg,
Nett,
Rounds
FROM (
SELECT
Members.FirstName + ' ' + Members.LastName AS Name,
CASE
WHEN MenuCountry.ImgURL IS NULL THEN
'~/images/flags/ismygolf.png'
ELSE
MenuCountry.ImgURL
END AS FlagImg,
AVG(CAST(NewScores.NetScore AS DECIMAL(18, 4))) AS Nett,
COUNT(Score.ScoreID) AS Rounds
FROM
Members
INNER JOIN
Score NewScores
ON Members.MemberID = NewScores.MemberID
LEFT OUTER JOIN MenuCountry
ON Members.Country = MenuCountry.ID
WHERE
Members.Status = 1
AND NewScores.InsertedDate >= DATEADD(mm, -3, GETDATE())
GROUP BY
Members.FirstName + ' ' + Members.LastName,
MenuCountry.ImgURL
) AS Dertbl
ORDER BY;
What you are looking for is a query with WITH clause, if your dbms supports it. Then
WITH NewScores AS (
SELECT *
FROM Score
WHERE InsertedDate >= DATEADD(mm, -3, GETDATE())
)
SELECT
<and the rest of your query>
;
Note that there is no ; in the first half. HTH.
You are missing table NewScores, so it can't be found. Just join this table.
If you really want to avoid joining it directly you can replace NewScores.NetScore with SELECT NetScore FROM NewScores WHERE {conditions on which they should be matched}

Running sum with aggregate function

I am retrieving the results of the mlog table and calculate the subtotal of the qtyn with the help of following code 1. I am stuck with how to join my second code criteria with the first.
Thanks for any help
1.
SELECT autn, date, itcode, qtyn, out,
date, phstock,
qtyn + COALESCE(
(SELECT SUM(qtyn) FROM dbo.mlog b
WHERE b.autn < a.autn
AND itcode = '40'), 0) AS balance
FROM dbo.mlog a
WHERE (itcode = '40')
ORDER BY autn
2.
date >=(SELECT MAX([date]) FROM mlog)
To append a condition to the code, use AND or OR. EG:
SELECT a.autn, a.date, a.itcode, a.qtyn, a.out,
a.date, a.phstock,
a.qtyn + COALESCE(
(SELECT SUM(b.qtyn) FROM dbo.mlog b
WHERE b.autn < a.autn
AND b.itcode = '40'), 0) AS balance
FROM dbo.mlog a
WHERE (a.itcode = '40' AND a.date >= (SELECT MAX([c.date]) FROM mlog c) )
ORDER BY a.autn
Not tested, but should do what you want
I have heard that SQL Server is rather inefficient with coalesce(), because it runs the first part twice. Here is an alternative way of writing this:
with ml as (
SELECT ml.autn, ml.date, ml.itcode, ml.qtyn, ml.out, ml.date, ml.phstock
FROM dbo.mlog ml
WHERE ml.itcode = '40' AND ml.date >= (SELECT MAX(ml1.date]) FROM mlog ml1)
)
select ml.*,
(select sum(m1l.qtyn) from ml ml1 where ml1.autn <= ml.autn) as balance
from ml
ORDER BY ml.autn
I also wonder if the where clause would be more efficient as:
WHERE ml.itcode = '40' AND ml.date = (SELECT top 1 ml1.date FROM mlog ml1 order by ml1.date desc)