Calculate Sub Query Column Based On Calculated Column - sql

I have a table ScheduleRotationDetail that contains these as columns:
ScheduleRotationID ScheduleID Ordinal Duration
379 61 1 1
379 379 2 20
379 512 3 1
379 89 4 20
I have a query that goes like this in order to get the day of the year each schedule is supposed to start on:
SELECT ScheduleID, Ordinal, Duration,
,Duration * 7 AS DurationDays
,( SELECT ( ISNULL( SUM(ISNULL( Duration, 0 )), 0 ) - 1 ) * 7
FROM ScheduleRotationDetail WHERE ScheduleRotationID = srd.ScheduleRotationID
AND Ordinal <= srd.Ordinal ) AS StartDay
FROM ScheduleRotationDetail srd
WHERE srd.ScheduleRotationID = 379
That outputs this as the result set:
ScheduleID Ordinal Duration DurationDays StartDay
61 1 1 7 0
379 2 20 140 140
512 3 1 7 147
89 4 20 140 287
Yet what I need the start day column values to be are:
0
7
147
154
I have tried CTEs but can't get it to work so I've come to here for advice.

It looks like you want a cumulative sum. In SQL Server 2012+, you can do:
SELECT ScheduleID, Ordinal, Duration,
SUM(Duration*7) OVER (ORDER BY Ordinal) - Duration*7 as StartDate
FROM ScheduleRotationDetail srd ;
In earlier versions, you can use APPLY for this purpose (or a correlated subquery).

Related

range between interval that does not include the first row

I have a line:
sum(purchases) over(partition by category order by value_day range between interval '1' month preceding and current row)
If value_day = Aug 21, it returns sum from and included July 21 till and included Aug 21, but I need from and included July 22 till and included Aug 21.
How can I do that?
You can use an expression to define the starting point of the window. So you can
Subtract a month from the current date
Add a day to it
Giving something like:
sum ( purchases ) over (
partition by category
order by value_day
range between ( value_day - ( add_months ( value_day, -1 ) + 1 ) ) preceding
and current row
)
You can either:
Use two windowed functions, your one to add everything from the past month and then subtract a second one that just covers the range you do not want to include; or
Use a correlated sub-query rather than windowed analytic functions.
SELECT t.*,
sum(purchases) over(
partition by category
order by value_day
range between interval '1' month preceding and current row
) -
COALESCE(
sum(purchases) over(
partition by category
order by value_day
range between interval '1' month preceding and interval '1' month preceding
),
0
) AS total1,
( SELECT SUM(s.purchases)
FROM table_name s
WHERE t.category = s.category
AND ADD_MONTHS(t.value_day, -1) + INTERVAL '1' DAY <= s.value_day
AND s.value_day <= t.value_day
) AS total2
FROM table_name t;
Which, for the sample data:
CREATE TABLE table_name (category, value_day, purchases) AS
SELECT 1, DATE '2022-01-01' + LEVEL - 1, LEVEL
FROM DUAL
CONNECT BY LEVEL <= 50;
Outputs:
CATEGORY
VALUE_DAY
PURCHASES
TOTAL1
TOTAL2
...
...
...
...
...
1
01-FEB-22
32
527
527
1
02-FEB-22
33
558
558
1
03-FEB-22
34
589
589
1
04-FEB-22
35
620
620
1
05-FEB-22
36
651
651
1
06-FEB-22
37
682
682
1
07-FEB-22
38
713
713
1
08-FEB-22
39
744
744
1
09-FEB-22
40
775
775
1
10-FEB-22
41
806
806
1
11-FEB-22
42
837
837
1
12-FEB-22
43
868
868
1
13-FEB-22
44
899
899
1
14-FEB-22
45
930
930
1
15-FEB-22
46
961
961
1
16-FEB-22
47
992
992
1
17-FEB-22
48
1023
1023
1
18-FEB-22
49
1054
1054
1
19-FEB-22
50
1085
1085
db<>fiddle here

sql sum with separate column for each day

My current code looks like this:
declare #start datetime
declare #end datetime
set #start = '2/16/2020'
set #end = '2/19/2020'
select
s.location, s.department, s.position, SUM(s.hours)/60
from SCHEDULES s where SCHDATE between #start and #end
group by s.location, s.department, s.position
It yields the following results (which is correct):
loc dep pos hrs
2 2 7 96
3 2 11 96
2 2 13 192
3 2 5 96
3 1 4 228
How do I break this out by day so that the format looks like below:
'start' is the #start variable and 'start+1' is simply that plus 1 day, etc.
loc dep pos start start+1 start+2 start+3
2 2 7 24 24 24 24
3 2 11 24 24 24 24
2 2 13 48 48 48 48
3 2 5 24 24 24 24
3 1 4 57 57 57 57
thanks
Sounds like you want to do a pivot:
SELECT *
FROM SCHEDULES s
PIVOT(
SUM(hours)
FOR SCHDATE IN (
[2020-2-16],
[2020-2-17],
[2020-2-18],
[2020-2-19])
) AS pivot_table;
Hopefully the dates you want to work with are fixed and known. If you need to pivot on calculated columns, things seem to get a lot more complicated. For example, see this thread.

Difference of values that belong to same group but stored in two rows

I have a problem where i need to fetch 2 specific records with 2 different value and find the difference between their amount. This needs to be done for each device.
Lets take the following table as example
DevID reason amount DateTime
--------------------------------------------------
99 5 84 18-12-2016 18:10
99 0 35 18-12-2016 18:11
99 0 80 18-12-2016 18:12
99 0 34 18-12-2016 18:15
23 5 36 18-12-2016 18:16
23 4 22 18-12-2016 18:17
23 1 22 18-12-2016 18:18
23 2 22 18-12-2016 18:19
99 2 11 18-12-2016 18:20
99 8 50 18-12-2016 18:21
99 0 23 18-12-2016 18:22
99 5 06 18-12-2016 18:25
99 8 12 18-12-2016 18:30
So my reason of interest is 5 and 8. 5 is device logon and 8 is logout and other numbers refer to other things.
I want to fetch records with device logon reason(5) and the next device logout(8) and find the difference in its amount value so in the table above for device 99, amount for reason 5 is 84 and the logout event(8) is 50, so the difference is 34 which if greater than 10 i need list that device.
(please note there is another case of 5 and 8 for the same record, the difference is not greater than 5) but the first set has diff greater than 10 so we need to display that device id
So the expected output for the above is
DevID
-------
99
i was thinking of join
Join table A which has all records with 5(sorted by deviceid,date) and table B which has all records of with 8 and then subtract their amounts and display the records with value greater than 10.
Not sure if join is the way to go? any simpler/fast solution?
You can use LEAD function to match logon time with logout:
WITH cte AS (
SELECT devid
, reason
, amount
, LEAD(reason) OVER (PARTITION BY devid ORDER BY datetime) AS next_reason
, LEAD(amount) OVER (PARTITION BY devid ORDER BY datetime) AS next_amount
FROM t
WHERE reason IN (5, 8)
)
SELECT *, amount - next_amount AS diff
FROM cte
WHERE reason = 5 -- logon
AND next_reason = 8 -- next event is a logout
AND amount - next_amount >= 10 -- difference of current and next
You can get the next "8" value using window functions. Then join and filter:
select t.*,
(t.value - t8.value) as diff
from (select t.*,
min(case when reason = 8 then datetime end) over (partition by devid order by datetime desc) as next_8_datetime
from t
) t left join
t t8
on t8.devid = t.devid and
t8.datetime = t.next_8_datetime and
t8.reason = 8
where t.reason = 5
order by diff desc
limit 1;

T-SQL Group by day date but i want show query full date

I want to show the date field can not group.
My Query:
SELECT DAY(T1.UI_CreateDate) AS DATEDAY, SUM(1) AS TOTALCOUNT
FROM mydb.dbo.LP_UseImpression T1 WHERE T1.UI_BR_BO_ID = 45
GROUP BY DAY(T1.UI_CreateDate)
Result:
DATEDAY TOTALCOUNT
----------- -----------
15 186
9 1
3 2
26 481
21 297
27 342
18 18
30 14
4 183
25 553
13 8
22 469
16 1
17 28
20 331
28 90
14 33
8 1
But i want to show the full date...
Example result:
DATEDAY TOTALCOUNT
----------- -----------
15/06/2015 186
9/06/2015 1
3/06/2015 2
26/06/2015 481
21/06/2015 297
27/06/2015 342
18/06/2015 18
30/06/2015 14
4/06/2015 183
25/06/2015 553
13/06/2015 8
22/06/2015 469
16/06/2015 1
17/06/2015 28
20/06/2015 331
28/06/2015 90
14/06/2015 33
8/06/2015 1
I want to see the results...
I could not get a kind of results...
How can I do?
Thanx!
How about just casting to date to remove any time component:
SELECT CAST(T1.UI_CreateDate as DATE) AS DATEDAY, COUNT(*) AS TOTALCOUNT
FROM mydb.dbo.LP_UseImpression T1
WHERE T1.UI_BR_BO_ID = 45
GROUP BY CAST(T1.UI_CreateDate as DATE)
ORDER BY DATEDAY;
SUM(1) for calculating the count does work. However, because SQL has the COUNT(*) function, it seems a bit awkward.
So you can group by DAY(T1.UI_CreateDate) or use full date for grouping. But these are different . As both these dates '2015-04-15' and '2015-12-15' result in same DAY value of 15.
Assuming you want to group on DAY rather than date please try the below version of query:
SELECT DISTINCT
T1.UI_CreateDate as DATEDAY,
count(1) over (PARTITION BY DAY(T1.UI_CreateDate) ) AS TOTALCOUNT
FROM mydb.dbo.LP_UseImpression T1 WHERE T1.UI_BR_BO_ID = 45
sql fiddle for demo: http://sqlfiddle.com/#!6/c3337/1

SQL order by 100% not ordering first

I have a query that shows my counter's accuracy and I'm doing a simple order by accuracy. But, what is happening it is ordering correctly BUT 100% appears at the bottom and not in correct order. I have moved the order by, but I may be missing something?
QUERY:
select counted_by_user_id, total, defects, trunc((total-defects)/total*100,2)||'%' as accuracy from
(
select counted_by_user_id, sum(locations) as total, sum(numberOfDefectBins) as defects from
(
select counted_by_user_id, locations, numberOfDefectBins from
(
select trunc(ical.last_updated_date_utc) as CountDate,ip.process_name,ipl.icqa_process_id,ical.counted_by_user_id,count(*) as locations, (
SELECT COUNT(iid.icqa_ical_detail_id)
FROM icqa_process_locations ipl1, icqa_count_attempt_logs ical1, icqa_ical_details iid
WHERE ipl1.icqa_process_id = ipl.icqa_process_id
AND ical1.icqa_count_attempt_id = ipl1.icqa_count_attempt_id
AND ical1.counted_by_user_id = ical.counted_by_user_id
AND iid.icqa_count_attempt_log_id = ical1.icqa_count_attempt_log_id
AND iid.is_defective = 'Y' and trunc(ipl1.last_updated_date_utc) = trunc(sysdate)) as numberOfDefectBins
from icqa_count_attempt_logs ical left join icqa_process_locations ipl
on ical.icqa_count_attempt_id = ipl.icqa_count_attempt_id
left join ICQA_PROCESSES ip on ipl.icqa_process_id= ip.icqa_process_id
where trunc(ical.last_updated_date_utc) = trunc(sysdate)
AND ical.counted_by_user_id IS NOT NULL
group by trunc(ical.last_updated_date_utc), ip.process_name, ipl.icqa_process_id, ical.counted_by_user_id
order by ical.counted_by_user_id
))
group by counted_by_user_id
)
order by accuracy desc;
RESULT:
counted_by_user_id total defects accuracy
dggonza 346 1 99.71%
giermanc 225 1 99.55%
kylecoll 659 4 99.39%
manansal 71 1 98.59%
jssuarez 271 5 98.15%
jhheredi 464 10 97.84%
tabilinl 185 4 97.83%
darinc 102 3 97.05%
tostab 484 18 96.28%
alicmena 25 1 96%
reyesk 733 31 95.77%
genej 478 22 95.39%
yadirac 73 4 94.52%
lhherold 505 28 94.45%
anamarih 465 30 93.54%
pineiror 380 25 93.42%
nallelys 349 31 91.11%
almquij 112 12 89.28%
kustance 357 50 85.99%
arteagaa 54 12 77.77%
gardne 848 0 100%
willij 5 0 100%
castnera 21 0 100%
pbarbara 43 0 100%
caudilr 493 0 100%
jennifei 27 0 100%
Of course. Because you are sorting a string not a number.
There are multiple solutions:
order by length(accuracy) desc, accuracy;
is probably the easiest.
Some others:
order by cast(replace(accuracy, '%', '') as float);
order by (total-defects)/total;
order by (case when accuracy = '100%' then 1
when accuracy >= '10%' then 2
else 3
end), accuracy desc