Not getting desired output while doing calculation in sql query result - sql

I have one query result as the following format.
Price Quarter
80 Q1
40 Q2
I need to calculate %Value and need to display 'NA' for Q1 and 2 needs to display for Q2
Desired Result is
Price Quarter %Value
80 Q1 NA
40 Q2 2=(80/40)
How will I get the desired result?

Hmmm. Your results are very specific. The following might be what you want:
select price, quarter,
(case when quarter = 'Q1' then NULL
else value * 1.0 / sum(value) over ()
end) as col
from q;

Any question of the form compute function of values in two adjacent rows (in some order) is answered by joining the table to itself, where the join is based on the "previous" value. See my running totals example for an explanation.
Your code will look something like this. The data you provide isn't sufficient to give a complete answer (no year, no identifying key) but I hope this will get you started.
select Q.price, Q.quarter, Q.value/P.value as '%value'
from (
select a.quarter, a.value, max(b.quarter) as prior
from T as a left join T as b
on a.quarter > b.quarter
group by a.price, a.quarter
) as Q
join T as P -- prior
on Q.prior = P.quarter

Related

SQL SELECT filtering out combinations where another column contains empty cells, then returning records based on max date

I have run into an issue I don't know how to solve. I'm working with a MS Access DB.
I have this data:
I want to write a SELECT statement, that gives the following result:
For each combination of Project and Invoice, I want to return the record containing the maximum date, conditional on all records for that combination of Project and Invoice being Signed (i.e. Signed or Date column not empty).
In my head, first I would sort the irrelevant records out, and then return the max date for the remaining records. I'm stuck on the first part.
Could anyone point me in the right direction?
Thanks,
Hulu
Start with an initial query which fetches the combinations of Project, Invoice, Date from the rows you want returned by your final query.
SELECT
y0.Project,
y0.Invoice,
Max(y0.Date) AS MaxOfDate
FROM YourTable AS y0
GROUP BY y0.Project, y0.Invoice
HAVING Sum(IIf(y0.Signed Is Null,1,0))=0;
The HAVING clause discards any Project/Invoice groups which include a row with a Null in the Signed column.
If you save that query as qryTargetRows, you can then join it back to your original table to select the matching rows.
SELECT
y1.Project,
y1.Invoice,
y1.Desc,
y1.Value,
y1.Signed,
y1.Date
FROM
YourTable AS y1
INNER JOIN qryTargetRows AS sub
ON (y1.Project = sub.Project)
AND (y1.Invoice = sub.Invoice)
AND (y1.Date = sub.MaxOfDate);
Or you can do it without the saved query by directly including its SQL as a subquery.
SELECT
y1.Project,
y1.Invoice,
y1.Desc,
y1.Value,
y1.Signed,
y1.Date
FROM
YourTable AS y1
INNER JOIN
(
SELECT y0.Project, y0.Invoice, Max(y0.Date) AS MaxOfDate
FROM YourTable AS y0
GROUP BY y0.Project, y0.Invoice
HAVING Sum(IIf(y0.Signed Is Null,1,0))=0
) AS sub
ON (y1.Project = sub.Project)
AND (y1.Invoice = sub.Invoice)
AND (y1.Date = sub.MaxOfDate);
Write A SQL query, which should be possible in MS-Access too, like this:
SELECT
Project,
Invoice,
MIN([Desc]) Descriptions,
SUM(Value) Value,
MIN(Signed) Signed,
MAX([Date]) "Date"
FROM data
WHERE Signed<>'' AND [Date]<>''
GROUP BY
Project,
Invoice
output:
Project
Invoice
Descriptions
Value
Signed
Date
A
1
Ball
100
J.D.
2022-09-20
B
1
Sofa
300
J.D.
2022-09-22
B
2
Desk
100
J.D.
2022-09-23
Note: for invoice 1 on project A, you will see a value of 300, which is the total for that invoice (when grouping on Project='A' and Invoice=1).
Maybe I should have used DCONCAT (see: Concatenation in between records in Access Query ) for the Description, to include 'TV' in it. But I am unable to test that so I am only referring to this answer.
Try joining a second query:
Select *
From YourTable As T
Inner Join
(Select Project, Invoice, Max([Date]) As MaxDate
From YourTable
Group By Project, Invoice) As S
On T.Project = S.Project And T.Invoice = S.Invoice And T.Date = S.MaxDate

Under the impression that these two SQL queries would give the same output, yet they have wildly different results

I am using pandasql. The first turns values as expected, but the second returns things that shouldn't even exist. However, I would expect them to return the same value. The only difference, to my eye, is that in the first one, the grouping/sum occurs within the subqueries, while it occurs outside of them in the second one. What am I missing? Thank you for the help! (Outputs at bottom)
First query (the correct one)
SELECT a.'Name', a.Q1, b.Q2, (a.Q1 + b.Q2) AS Total
FROM
(SELECT c.'Name', SUM(c.'Paid Amount') AS Q1
FROM some_data AS c
WHERE c.'Quarter' = 'Q1'
GROUP BY c.'Name') AS a
JOIN
(SELECT d.'Name', SUM(d.'Paid Amount') AS Q2
FROM some_data AS d
WHERE d.'Quarter' = 'Q2'
GROUP BY d.'Name') AS b
ON a.'Name' = b.'Name'
ORDER BY Total DESC
LIMIT 5;
Second query (the bad one)
SELECT a.'Name' as Label, SUM(a.'Paid Amount') AS Q1, SUM(b.'Paid Amount') AS Q2, (SUM(a.'Paid Amount') + SUM(b.'Paid Amount')) as Total
FROM
(SELECT c.'Name', c.'Paid Amount'
FROM some_data AS c
WHERE c.'Quarter' = 'Q1') AS a
JOIN
(SELECT c.'Name', c.'Paid Amount'
FROM some_data AS c
WHERE c.'Quarter' = 'Q2') AS b
ON a.'Name' = b.'Name'
GROUP BY Label
ORDER BY Total DESC
LIMIT 5;
I threw some random data together to demonstrate the problem.
Output from the first query (expected)
Output from second query (problematic)
This is what I call wishful coding.
I hope you realize that doing the aggregation before joining produces the correct answer.
The issue is that JOIN can both multiply the number of rows and remove rows. In your case, the issue is that one or both tables have multiple rows for name and this multiplies the number of rows. The SUM() just adds up all the values produced as a results of the JOIN.
Note: Conditional aggregation is a much simpler way to write the query:
SELECT c.Name,
SUM(CASE WHEN c.Quarter = 'Q1' THEN c.PaidAmount END) AS Q1
SUM(CASE WHEN c.Quarter = 'Q2' THEN c.PaidAmount END) AS Q2
FROM some_data AS c
WHERE c.Quarter IN ('Q1', 'Q2')
GROUP BY c.Name

get the incremental pattern

I have table as below
Item|Year|Price
---------------
C|2010|50
C|2000|40
C|1999|30
A|2010|10
A|2009|15
B|2018|10
B|2017|100
B|2015|750
D|2018|220
D|2017|200
D|2016|185
I want to write a query so that I get only the item name which have price in incremental order every greater year.
The output of above pattern would be
ITEM
----
D
C
(D and C only have incremental price for each higher year)
I tried with self Join but I am not able the required output
I think this query will give you the results you want. It uses a self-join to find all years where an item has a price which is lower than a previous year. Items which have no later years where the price is lower (which will appear as i2.Item=NULL) will have a COUNT(i2.Item)=0:
SELECT i1.Item
FROM Items i1
LEFT JOIN Items i2 ON i2.Item = i1.Item AND i2.Price < i1.Price AND i2.Year > i1.Year
GROUP BY i1.Item
HAVING COUNT(i2.Item) = 0
Output:
Item
C
D
SQLFiddle Demo
You may use lag analytical function as in the following select statement :
select item
from
(
select sign(price - lag(price,1,0) over (order by year)) val,
t.item
from tab t
)
group by item
having avg(val)=1;
ITEM
----
D
C
SQL Fiddle Demo

Get sum of previous 6 values including the group

I need to sum up the values for the last 7 days,so it should be the current plus the previous 6. This should happen for each row i.e. in each row the column value would be current + previous 6.
The case :-
(Note:- I will calculate the hours,by suming up the seconds).
I tried using the below query :-
select SUM([drivingTime]) OVER(PARTITION BY driverid ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW)
from [f.DriverHseCan]
The problem I face is I have to do grouping on driver,asset for a date
In the above case,the driving time should be sumed up and then,its previous 6 rows should be taken,
I cant do this using rank() because I need these rows as well as I have to show it in the report.
I tried doing this in SSRS and SQL both.
In short it is adding total driving time for current+ 6 previous days
Try the following query
SELECT
s.date
, s.driverid
, s.assetid
, s.drivingtime
, SUM(s2.drivingtime) AS total_drivingtime
FROM f.DriverHseCan s
JOIN (
SELECT date,driverid, SUM(drivingtime) drivingtime
FROM f.DriverHseCan
GROUP BY date,driverid
) AS s2
ON s.driverid = s2.driverid AND s2.date BETWEEN DATEADD(d,-6,s.date) AND s.date
GROUP BY
s.date
, s.driverid
, s.assetid
, s.drivingtime
If you have week start/end dates, there could be better performing alternatives to solve your problem, e.g. use the week number in SSRS expressions rather than do the self join on SQL server
I think aggregation does what you want:
select sum(sum([drivingTime])) over (partition by driverid
order by date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
)
from [f.DriverHseCan]
group by driverid, date
I guess you need to use CROSS APPLY.
Something like following? :
SELECT driverID,
date,
CA.Last6DayDrivingTime
FROM YourTable YT
CROSS APPLY
(
SELECT SUM(drivingTime) AS Last6DayDrivingTime
FROM YourTable CA ON CA.driverID=YT.driverID
WHERE CA.date BETWEEN DATEADD(DAY,-6,YT.date) AND YT.date)
) CA
Edit:
As you commented that cross apply slow down the performance, other option is to pre calculate the week values in temp table or using CTE and then use them in your main query.

SQL Server select max date per ID

I am trying to select max date record for each service_user_id for each finance_charge_id and the amount that is linked the highest date
select distinct
s.Finance_Charge_ID, MAX(s.start_date), s.Amount
from
Service_User_Finance_Charges s
where
s.Service_User_ID = '156'
group by
s.Finance_Charge_ID, s.Amount
The issue is that I receive multiple entries where the amount is different. I only want to receive the amount on the latest date for each finance_charge_id
At the moment I receive the below which is incorrect (the third line should not appear as the 1st line has a higher date)
Finance_Charge_ID (No column name) Amount
2 2014-10-19 1.00
3 2014-10-16 500.00
2 2014-10-01 1000.00
Remove the Amount column from the group by to get the correct rows. You can then join that query onto the table again to get all the data you need. Here is an example using a CTE to get the max dates:
WITH MaxDates_CTE (Finance_Charge_ID, MaxDate) AS
(
select s.Finance_Charge_ID,
MAX(s.start_date) MaxDate
from Service_User_Finance_Charges s
where s.Service_User_ID = '156'
group by s.Finance_Charge_ID
)
SELECT *
FROM Service_User_Finance_Charges
JOIN MaxDates_CTE
ON MaxDates_CTE.Finance_Charge_ID = Service_User_Finance_Charges.Finance_Charge_ID
AND MaxDates_CTE.MaxDate = Service_User_Finance_Charges.start_date
This can be done using a window function which removes the need for a self join on the grouped data:
select Finance_Charge_ID,
start_date,
amount
from (
select s.Finance_Charge_ID,
s.start_date,
max(s.start_date) over (partition by s.Finance_Charge_ID) as max_date,
s.Amount
from Service_User_Finance_Charges s
where s.Service_User_ID = 156
) t
where start_date = max_date;
As the window function does not require you to use group by you can add any additional column you need in the output.