Get minimum value per user - sql

I have this SQL code here, my wish is to calculate the latest fee balance per student for each deposit made. So far I have been able to achieve this but now the question is how I'm I to select only the latest balance while disregarding the other previous entries/Balances
(select
f.Totals -SUM(Total) OVER(ORDER BY pay_Id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as Balance,
p.Total as 'TotalPaid',
c.class_id as 'ClassID',
p.std_ID as 'STDID',
c.class_name as 'Class',
a.ad_stdname as 'Name',
t.term_id as 'TermID',
t.term_name as 'Term',
p.Date as 'Date',
case when (st.str_id = null) then '-' else st.str_id end as 'StrID',
case when (p.Tution_fee = null) then '0' else p.Tution_fee end as 'Tution',
case when (p.adm_fee = null) then '0' else p.adm_fee end as 'Admission',
case when (p.Activity_fee = null) then '0' else p.Activity_fee end as 'Activity',
case when (p.Textbk_Statio = null) then '0' else p.Textbk_Statio end as 'Texbooks',
case when (p.Transport_fee = null) then '0' else p.Transport_fee end as 'Transport'
from Payments p
inner join Admissions a
on a.ad_id = p.std_ID
inner join classes c
on c.class_id =p.class_id
inner join fees f on f.fee_classID = p.class_id and f.Term_id = p.Term_id
left join streams st
on st.str_id = p.str_id
inner join terms t
on p.Term_id = t.term_id
where a.ad_id = 29
)
order by p.std_name,Balance,p.Date desc
This is my current output

I have selected min value for each user in below example:
user date value
Sonu 1/2/2010 1.5
Monu 1/3/2010 2.5
Arun 8/4/2009 3.5
Sonu 2/2/2010 1.0
Monu 12/2/2009 0.5
select t.username, t.date, t.value
from MyTable t
inner join (
select username, min(value) as value
from MyTable
group by username
) tm on t.username = tm.username and t.value = tm.value
Demo sql Fiddle
Use above logic to implement in your code
I tried to accommodate above logic in you query. for now i have just used stdid for grouping, you may add more accordingly. you might get some syntax error:
with MyTable as (select
f.Totals -SUM(Total) OVER(ORDER BY pay_Id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as Balance,
p.Total as 'TotalPaid',
c.class_id as 'ClassID',
p.std_ID as 'STDID',
c.class_name as 'Class',
a.ad_stdname as 'Name',
t.term_id as 'TermID',
t.term_name as 'Term',
p.Date as 'Date',
case when (st.str_id = null) then '-' else st.str_id end as 'StrID',
case when (p.Tution_fee = null) then '0' else p.Tution_fee end as 'Tution',
case when (p.adm_fee = null) then '0' else p.adm_fee end as 'Admission',
case when (p.Activity_fee = null) then '0' else p.Activity_fee end as 'Activity',
case when (p.Textbk_Statio = null) then '0' else p.Textbk_Statio end as 'Texbooks',
case when (p.Transport_fee = null) then '0' else p.Transport_fee end as 'Transport'
from Payments p
inner join Admissions a
on a.ad_id = p.std_ID
inner join classes c
on c.class_id =p.class_id
inner join fees f on f.fee_classID = p.class_id and f.Term_id = p.Term_id
left join streams st
on st.str_id = p.str_id
inner join terms t
on p.Term_id = t.term_id
where a.ad_id = 29
order by p.std_name,Balance,p.Date desc) select * from MyTable
inner join
(
select STDID, min(Balance) as value
from MyTable
group by STDID
) tm on t.STDID = tm.STDID and t.Balance = tm.Balance

Related

How to select only unique values from this table based on criteria?

I want to select only the first record from each group. So basically I only want the data for first processing date column and don't want the rest of the date for each product.
My current code as below
SELECT
C.AccountID as ABN_ACC ,
C.ProductSymbol,
C.ProductShortName,
S.ReactorProduct AS REC_PROD,
C.CurrencyCode AS PROD_CCY,
C.CountryOfPayment AS PAYCONTRY,
C.DividendValueCur AS PAYCCY,
C.DividendValue AS DIV_AMNT,
CONCAT ((IIF (C.QuantitySettledNoTax_LS = 'S' ,(-1*C.QuantitySettledNoTax),C.QuantitySettledNoTax )),(IIF(C.QuantitySettledTax_LS = 'S',(-1*C.QuantitySettledTax),QuantitySettledTax))) AS DIVQTY ,
C.ExdividendDate AS EXDATE,
C.Recorddate AS RECDATE,
C.DividendPayDate AS PAYDATE
From "LiquidCDW". [Staging].[CA_CorporateActions] AS C
RIGHT JOIN "LiquidCDW".[Staging].[POS_SettledPositions] AS P ON C.Recorddate = P.ProcessingDate AND C.AccountID = p.AccountID AND C.ProductSymbol = P.ProductSymbol AND C.DividendValueCur = P.CurrencyCode
INNER JOIN "LiquidCDW".[Transformation].[Mapping_Product_ABNReactor] AS S ON C.ProductSymbol = S.ReactorProduct AND S.ValidTo IS NULL AND S.ProductType = 'E' AND c.DividendValueCur = S.Currency
where C.ExdividendDate >= '20210201'
ORDER by C.ProductSymbol , C.CurrencyCode
One approach uses ROW_NUMBER:
WITH cte AS (
SELECT
ROW_NUMBER() OVER (PARTITION BY C.ProductSymbol ORDER BY processingDate) rn,
C.AccountID AS ABN_ACC,
C.ProductSymbol,
C.ProductShortName,
S.ReactorProduct AS REC_PROD,
C.CurrencyCode AS PROD_CCY,
C.CountryOfPayment AS PAYCONTRY,
C.DividendValueCur AS PAYCCY,
C.DividendValue AS DIV_AMNT,
CONCAT((IIF(C.QuantitySettledNoTax_LS = 'S',
(-1*C.QuantitySettledNoTax), C.QuantitySettledNoTax)),
(IIF(C.QuantitySettledTax_LS = 'S',
(-1*C.QuantitySettledTax),QuantitySettledTax))) AS DIVQTY,
C.ExdividendDate AS EXDATE,
C.Recorddate AS RECDATE,
C.DividendPayDate AS PAYDATE
FROM [LiquidCDW].[Staging].[CA_CorporateActions] AS C
RIGHT JOIN "LiquidCDW".[Staging].[POS_SettledPositions] AS P
ON C.Recorddate = P.ProcessingDate AND
C.AccountID = p.AccountID AND
C.ProductSymbol = P.ProductSymbol AND
C.DividendValueCur = P.CurrencyCode
INNER JOIN [LiquidCDW].[Transformation].[Mapping_Product_ABNReactor] AS S
ON C.ProductSymbol = S.ReactorProduct AND
S.ValidTo IS NULL AND
S.ProductType = 'E' AND
C.DividendValueCur = S.Currency
WHERE
C.ExdividendDate >= '20210201'
)
SELECT *
FROM cte
WHERE rn = 1
ORDER BY ProductSymbol, CurrencyCode;

Condensing duplicate data while maintaining multiple entries per client

I apologize if the title is not descriptive enough. I am having a hard time describing what I am looking for.
I understand this is normally done on the front end but a client is requesting to have this data displayed in this way. this should show a single column with client information (lastname, firstname) and any/all booked appointments. Since there may be more than one appointment per client duplicate data will be displayed along each line. That will look like this:
lastName | firstName | apptDate | myStartTime | myEndTime
smith..............| john................| 4/7/2016........| 7:00.........................| 8:00
smith..............| john................| 4/9/2016........| 6:00.........................| 7:00
smith..............| john................| 4/14/2016......| 10:00.......................| 11:00
arnold.............| williams..........| 4/10/2016......| 7:00.........................| 11:00
arnold.............| williams..........| 4/11/2016......| 8:00.........................| 12:00
but I would like that to be displayed as:
smith..............| john................| 4/7/2016........| 7:00.........................| 8:00
................................................| 4/9/2016........| 6:00.........................| 7:00
................................................| 4/14/2016......| 10:00.......................| 11:00
arnold..............| williams.........| 4/10/2016.......| 7:00.........................| 11:00
................................................| 4/11/2016.......| 8:00.........................| 12:00
This is what I am currently working with:
SELECT
CASE
WHEN cli.rownum = 1 THEN cli.clientID
END AS 'clientID'
,CASE
WHEN cli.rownum = 1 THEN cli.lastName
END AS 'lastName'
,CASE
WHEN cli.rownum = 1 THEN cli.firstName
END AS 'firstName'
,CASE
WHEN cli.rownum = 1 THEN cli.homePhone
END AS 'home'
,CASE
WHEN cli.rownum = 1 THEN cli.cellPhone
END AS 'cell'
,info.*
,CASE
WHEN cli.rownum = 1 THEN cli.staffAlertMsg
END AS 'alert'
,cli.notes AS 'notes'
,CONVERT(varchar(10),cli.classDate,101) AS 'date'
,cli.myStartTime AS 'start'
,cli.myEndTime AS 'end'
,cli.typeName AS 'appointment'
FROM
(SELECT
c.clientID
,c.lastName
,c.firstName
,c.homePhone
,c.cellPhone
,info.*
,r.notes
,c.staffAlertMsg
,r.ClassDate
,r.myStartTime
,r.myEndTime
,vt.TypeName
,ROW_NUMBER()
OVER(PARTITION BY c.clientID ORDER BY c.clientID) AS 'rownum'
FROM clients c
LEFT OUTER JOIN tblReservation r
ON c.clientID = r.clientID
LEFT OUTER JOIN tblVisitTypes vt
ON vt.TypeID = r.visitType
OUTER APPLY
(SELECT
max(CASE
WHEN civ.clientIndexID = 4
THEN civ.clientIndexValueName
END) AS 'priority'
,max(CASE
WHEN civ.clientIndexID = 5
THEN civ.clientIndexValueName
END) AS 'lang'
,max(CASE
WHEN civ.clientIndexID = 17
THEN civ.clientIndexValueName
END) AS 'inter'
,max(CASE
WHEN ccf.name LIKE 'prac'
THEN ccv.TextVal
END) AS 'prac'
,max(CASE
WHEN st.TypeID = 100000001 THEN st.typeName
END) AS 'ride?'
,max(CASE
WHEN st.typeID = 100000006 THEN st.typeName
END) AS 'l?'
FROM tblClientCustomValues ccv
INNER JOIN tblClientCustomFields ccf
ON ccv.ID = ccf.ID
INNER JOIN tblClientIndexData cid
ON ccv.clientID = cid.clientID
INNER JOIN tblClientIndexValue civ
ON cid.clientIndexValueID = civ.clientIndexValueID
LEFT JOIN [type details] td
ON ccv.clientID = td.clientID
LEFT JOIN [student types] st
ON td.typeID = st.typeID
WHERE c.clientID = ccv.clientID
) info
) cli
WHERE cli.clientID != '-2'
AND cli.clientID != '0'
AND cli.clientID != '1'
AND (cli.classDate >= '4/1/2016'
AND cli.classDate <= '4/30/2016')
Assuming you want to only return the name for the first row of each client then this should work (also assuming a primary key column of ID on the clients table):
SELECT CASE WHEN cli.rownum = 1 THEN cli.lastName END AS lastName
,CASE WHEN cli.rownum = 1 THEN cli.firstName END AS firstName
,CONVERT(varchar(10),cli.apptDate,101) AS 'apptDate'
,cli.myStartTime
,cli.myEndTime
FROM
(SELECT c.ID
,c.lastName
,c.firstName
,r.apptDate
,r.myStartTime
,r.myEndTime
,ROW_NUMBER() OVER (PARTITION BY c.ID ORDER BY c.ID, r.apptDate, r.myStartTime) as rownum
FROM clients c
LEFT OUTER JOIN tblReservation r
ON c.clientID = r.clientID
LEFT OUTER JOIN tblVisitTypes vt
ON vt.TypeID = r.VisitType)
cli
ORDER BY cli.ID, cli.rownum
I have made both the join LEFT OUTER JOINS as your second INNER JOIN would have made the first join an INNER, which I assume is not what you wanted.

Sum of resulting set of rows in SQL

I've got the following query:
SELECT DISTINCT CU.permit_id, CU.month, /*CU.year,*/ M.material_id, M.material_name, /*MC.chemical_id, C.chemical_name,
C.precursor_organic_compound, C.non_precursor_organic_compound,*/
/*MC.chemical_percentage,*/
POC_emissions =
CASE
WHEN (C.precursor_organic_compound = 'true')
THEN (CU.chemical_usage_lbs / CU.material_density) * M.VOC
ELSE 0
END,
NON_POC_emissions =
CASE
WHEN (C.non_precursor_organic_compound = 'true')
THEN CU.chemical_usage_lbs * (MC.chemical_percentage / 100)
ELSE 0
END
FROM material M
LEFT OUTER JOIN material_chemical MC ON MC.material_id = M.material_id
LEFT OUTER JOIN chemical_usage CU ON CU.material_id = MC.material_id
LEFT OUTER JOIN chemical C ON C.chemical_id = MC.chemical_id
WHERE (CU.month >=1 AND CU.month <= 2)
AND CU.year = 2013
AND M.material_id = 52
--AND CU.permit_id = 2118
--GROUP BY CU.permit_id, M.material_id, M.material_name, CU.month, MC.chemical_id, MC.chemical_id, C.chemical_name, C.precursor_organic_compound, C.non_precursor_organic_compound
--ORDER BY C.chemical_name ASC
Which returns:
But what I need is to return one row per month per material adding up the values of POC per month and NON_POC per month.
So, I should end up with something like:
Month material_id material_name POC NON_POC
1 52 Krylon... 0.107581 0.074108687
2 52 Krylon... 0.143437 0.0988125
I tried using SUM but it sums up the same result multiple times:
SELECT /*DISTINCT*/ CU.permit_id, CU.month, /*CU.year,*/ M.material_id, M.material_name, /*MC.chemical_id, C.chemical_name,
C.precursor_organic_compound, C.non_precursor_organic_compound,*/
--MC.chemical_percentage,
POC_emissions = SUM(
CASE
WHEN (C.precursor_organic_compound = 'true')
THEN (CU.chemical_usage_lbs / CU.material_density) * M.VOC
ELSE 0
END),
NON_POC_emissions = SUM(
CASE
WHEN (C.non_precursor_organic_compound = 'true')
THEN CU.chemical_usage_lbs * (MC.chemical_percentage / 100)
ELSE 0
END)
FROM material M
LEFT OUTER JOIN material_chemical MC ON MC.material_id = M.material_id
LEFT OUTER JOIN chemical_usage CU ON CU.material_id = MC.material_id
LEFT OUTER JOIN chemical C ON C.chemical_id = MC.chemical_id
WHERE M.material_id = 52
--AND CU.permit_id = 187
AND (CU.month >=1 AND CU.month <= 2)
AND CU.year = 2013
GROUP BY CU.permit_id, M.material_id, M.material_name, CU.month/*, CU.year, MC.chemical_id, C.chemical_name, C.precursor_organic_compound, C.non_precursor_organic_compound*/
--ORDER BY C.chemical_name ASC
The first query has a DISTINCT clause. What is the output without the DISTINCT clause. I suspect you have more rows than shows in your screenshot.
Regardless, you could try something like this to get the desired result.
select permit_id, month, material_id, material_name,
sum(poc_emissions), sum(non_poc_emissions)
from (
SELECT DISTINCT CU.permit_id, CU.month, M.material_id, M.material_name,
POC_emissions =
CASE
WHEN (C.precursor_organic_compound = 'true')
THEN (CU.chemical_usage_lbs / CU.material_density) * M.VOC
ELSE 0
END,
NON_POC_emissions =
CASE
WHEN (C.non_precursor_organic_compound = 'true')
THEN CU.chemical_usage_lbs * (MC.chemical_percentage / 100)
ELSE 0
END
FROM material M
LEFT OUTER JOIN material_chemical MC ON MC.material_id = M.material_id
LEFT OUTER JOIN chemical_usage CU ON CU.material_id = MC.material_id
LEFT OUTER JOIN chemical C ON C.chemical_id = MC.chemical_id
WHERE (CU.month >=1 AND CU.month <= 2)
AND CU.year = 2013
AND M.material_id = 52
) main
group by permit_id, month, material_id, material_name
Explanation
Since the results you retrieved by doing a DISTINCT was consider source-of-truth, I created an in-memory table by making it a sub-query. However, this subquery must have a name of some kind...whatever name. I gave it a name main. Subqueries look like this:
select ... from (sub-query) <give-it-a-table-name>
Simple Example:
select * from (select userid, username from user) user_temp
Advanced Example:
select * from (select userid, username from user) user_temp
inner join (select userid, sum(debits) as totaldebits from debittable) debit
on debit.userid = user_temp.userid
Notice how user_temp alias for the subquery can be used as if the sub-query was a real table.
Use above query in subquery and group by (month) and select sum(POC_emissions) and sum(NON_POC_emissions )

Show unique value for id, based on latest createby date

I am using the following SQL code to JOIN a few tables, but need to show unique values for the s.incidentid based on the latest s.createdate.
I know I need to use a Sub query with Maxdate, but I am not sure on the correct syntax.
This is my first query with multiple joins and I am struggling to get my head round it.
Here is my code:
SELECT
s.incidentid,
u.internalid as AssignedTo,
u.fullname as AssignedTo_FullName,
s.createby as AssignedBy,
u2.fullname as AssignedBy_FullName,
s.createdate as AssignedTime,
i.[description],
i.fix,
st.[description] as [Status],
(SELECT (CASE WHEN u.internalid = s.createby THEN 'Yes' ELSE 'No' END) as SelfAssigned),
d.d1,
d.d2,
d.d3,
d.d4,
d.d5
FROM dbo.IncidentServiceLevelAgreement s
JOIN dbo.UserAll u on u.userid = s.userid
JOIN dbo.UserAll u2 on u2.internalid = s.createby
JOIN dbo.IncidentAll i on s.incidentid = i.incidentid
JOIN dbo.[Status] st on i.statusid = st.statusid
JOIN dbo.flatdiagnosis d on i.actualdiagnosisid = d.diagnosisid
WHERE (s.groupId = '4954' and s.incidentServiceLevelAgreementTypeID = '9')
ORDER BY AssignedTime DESC
Any help greatly appreciated.
The easiest is to use a CTE and the ROW_NUMBER function:
WITH CTE AS
(
SELECT RN = ROW_NUMBER() OVER ( PARTITION BY incidentid
ORDER BY createdate DESC ),
s.Incidentid,
u.Internalid AS AssignedTo,
u.Fullname AS AssignedTo_FullName,
s.Createby AS AssignedBy,
u2.Fullname AS AssignedBy_FullName,
s.Createdate AS AssignedTime,
i.[Description],
i.Fix,
st.[Description] AS [Status],
SelfAssigned = CASE WHEN u.Internalid = s.Createby
THEN 'Yes' ELSE 'No' END,
d.D1,
d.D2,
d.D3,
d.D4,
d.D5
FROM dbo.Incidentservicelevelagreement s
JOIN dbo.Userall u
ON u.Userid = s.Userid
JOIN dbo.Userall u2
ON u2.Internalid = s.Createby
JOIN dbo.Incidentall i
ON s.Incidentid = i.Incidentid
JOIN dbo.[Status] st
ON i.Statusid = st.Statusid
JOIN dbo.Flatdiagnosis d
ON i.Actualdiagnosisid = d.Diagnosisid
WHERE ( s.Groupid = '4954'
AND s.Incidentservicelevelagreementtypeid = '9' )
)
SELECT * FROM CTE WHERE RN = 1
ORDER BY AssignedTime DESC
(instead of SELECT * list all columns explicitly, I didn't feel like it)

Query for logistic regression, multiple where exists

A logistic regression is a composed of a uniquely identifying number, followed by multiple binary variables (always 1 or 0) based on whether or not a person meets certain criteria. Below I have a query that lists several of these binary conditions. With only four such criteria the query takes a little longer to run than what I would think. Is there a more efficient approach than below? Note. tblicd is a large table lookup table with text representations of 15k+ rows. The query makes no real sense, just a proof of concept. I have the proper indexes on my composite keys.
select patient.patientid
,case when exists
(
select c.patientid from tblclaims as c
inner join patient as p on p.patientid=c.patientid
and c.admissiondate = p.admissiondate
and c.dischargedate = p.dischargedate
where patient.patientid = p.patientid
group by c.patientid
having count(*) > 1000
)
then '1' else '0'
end as moreThan1000
,case when exists
(
select c.patientid from tblclaims as c
inner join patient as p on p.patientid=c.patientid
and c.admissiondate = p.admissiondate
and c.dischargedate = p.dischargedate
where patient.patientid = p.patientid
group by c.patientid
having count(*) > 1500
)
then '1' else '0'
end as moreThan1500
,case when exists
(
select distinct picd.patientid from patienticd as picd
inner join patient as p on p.patientid= picd.patientid
and picd.admissiondate = p.admissiondate
and picd.dischargedate = p.dischargedate
inner join tblicd as t on t.icd_id = picd.icd_id
where t.descrip like '%diabetes%' and patient.patientid = picd.patientid
)
then '1' else '0'
end as diabetes
,case when exists
(
select r.patientid, count(*) from patient as r
where r.patientid = patient.patientid
group by r.patientid
having count(*) >1
)
then '1' else '0'
end
from patient
order by moreThan1000 desc
I would start by using subqueries in the from clause:
select q.patientid, moreThan1000, moreThan1500,
(case when d.patientid is not null then 1 else 0 end),
(case when pc.patientid is not null then 1 else 0 end)
from patient p left outer join
(select c.patientid,
(case when count(*) > 1000 then 1 else 0 end) as moreThan1000,
(case when count(*) > 1500 then 1 else 0 end) as moreThan1500
from tblclaims as c inner join
patient as p
on p.patientid=c.patientid and
c.admissiondate = p.admissiondate and
c.dischargedate = p.dischargedate
group by c.patientid
) q
on p.patientid = q.patientid left outer join
(select distinct picd.patientid
from patienticd as picd inner join
patient as p
on p.patientid= picd.patientid and
picd.admissiondate = p.admissiondate and
picd.dischargedate = p.dischargedate inner join
tblicd as t
on t.icd_id = picd.icd_id
where t.descrip like '%diabetes%'
) d
on p.patientid = d.patientid left outer join
(select r.patientid, count(*) as cnt
from patient as r
group by r.patientid
having count(*) >1
) pc
on p.patientid = pc.patientid
order by 2 desc
You can then probably simplify these subqueries more by combining them (for instance "p" and "pc" on the outer query can be combined into one). However, without the correlated subqueries, SQL Server should find it easier to optimize the queries.
Example of left joins as requested...
SELECT
patientid,
ISNULL(CondA.ConditionA,0) as IsConditionA,
ISNULL(CondB.ConditionB,0) as IsConditionB,
....
FROM
patient
LEFT JOIN
(SELECT DISTINCT patientid, 1 as ConditionA from ... where ... ) CondA
ON patient.patientid = CondA.patientID
LEFT JOIN
(SELECT DISTINCT patientid, 1 as ConditionB from ... where ... ) CondB
ON patient.patientid = CondB.patientID
If your Condition queries only return a maximum one row, you can simplify them down to
(SELECT patientid, 1 as ConditionA from ... where ... ) CondA