DIFFICULT SQL issue [closed] - sql

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
Bear in mind this needs to be done in SQL - not PL/SQL, no while loops in play here.
I need to go from a single row with an amount, to multiple rows with the amount spread out. Here's the details;
I have a rule that awards in-lieu hours when an employee is short for a period. However, this rule awards ALL the hours on the last day of the period, and the client wants it spread out 8 hrs/day.
For example, if an EE is short 25 hours - the existing rule will award that 25 hours on the last day of the period. Client wants 8 hours on last day, 8 hours on 2nd last day, 8 hrs on 3rd last day, 1 hr on 4th last day.
My SQL is great, but I'm drawing a blank on this one. Any suggestions? Again, no PL/SQL or while loops allowed, has to be done in SQL (Oracle).
Sample data
Start with this;
**ID Date Amount**
1 09/30/2013 25
Need to end up with this;
**ID Date Amount**
1 09/30/2013 8
1 09/29/2013 8
1 09/28/2013 8
1 09/27/2013 1

Provided that those periods do not overlap for a single employee, you may also try something like this:
select distinct id, "date"-(level-1) "date", least(amount-(level-1)*8, 8) amount
from sample_data
connect by amount > (level-1)*8
order by id, "date" desc

If you create a table with all the possible days, you could then cross-join to that. Maybe that table has an actual date, but it also has a day number on each row: 1, 2, 3, 4... where that is the order in which you want to allocate the hours: i.e. 8 hours to day #1, any remaining (but max 8) to day # 2, and so on.
For any particular day_num, the expression ( (day_num - 1) * 8 ) will give you the max. hours that could be allocated already. Only if the employee has hours over this, does this particular day get an allocation. Also, the allocation has to be limited to 8.
The SQL would look something like below. (See this SQL fiddle for schema)
select emp_id, total_hours, day_num,
case
when total_hours - ((day_num-1) * 8) > 8 then 8
when total_hours - ((day_num-1) * 8) < 0 then 0
else total_hours - ((day_num-1) * 8)
end hours_for_day
from emp_short
cross join all_days
where total_hours - ((day_num-1) * 8) > 0

Related

SQL - Lead function to get dates from next row [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 10 months ago.
Improve this question
I have a table with a date and value for example:
AccID Date Value
1 01/01/2007 10
1 01/02/2008 20
1 01/03/2009 40
I want to create a new table that starts from the date in row 1 and ends with the date in row 2 and so on.....for example
AccID Date DateEnd Value
1 01/01/2007 01/02/2008 10
1 01/02/2008 01/03/2009 20
1 01/03/2009 01/04/2050 40
Select
date,
isnull(lead(date) over (partition by accID order by date), '01/04/2050') as DateEnd,
value
from column A
I have tried this code but I can't seem to get the correct output. This is the output I am currently getting
AccID Date DateEnd Value
1 01/02/2008 01/02/2009 20
1 01/02/2009 01/03/2007 40
1 01/01/2007 01/04/2050 10
You are not getting any output, you are getting an error
Incorrect syntax near 'partition'.
You get the correct results when you correct the error
Select
date,
isnull(lead(date) over (partition by accID order by date), '01/04/2050') as DateEnd,
value
from column A
Edit: Do yourself some favours and apply the following:
Avoid the use of reserved words or if you "must" use them, surround the column or table name with [ ] e.g. [date], [value],[column]
Research the function you're going to use to make the most of what it has to offer, to simplify your queries e.g. See the documentaion for Lead
Use date formats that are unambiguous e.g. '2050-04-01' in preference to '01/04/2050'. The latter could be either 1st April or 4th January depending on where in the world you happen to be

SQL query to get time spent on specific statuses based on single datetime column [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
I need to build a SQL query in which I can get time spent on multiple statuses (onHold,Waiting for customer,Resolved,Closed), so basically I do not want to include time spent on this statues and table looks like as below
So I need a query in which I can get actual time spent on ticket or time spent on status which I have mentioned so far I have tried below solutions and tried Cross APPLY but seems all did not help me as expected.
Tried below query so far and that gives me correct time spent on first status on-hold not after that:
SELECT t1.TICKETNUMBER,SUM(DATEDIFF(MINUTE,TICKETTIME,CloseTime)) as TotalMinutes
FROM [Admin].[TbtrnTicketHistory] t1
CROSS APPLY(SELECT TOP 1 TICKETTIME as CloseTime FROM [Admin].[TbtrnTicketHistory] t2 WHERE t1.TICKETNUMBER = t2.TICKETNUMBER and t2.TICKETHISTORYID > t1.TICKETHISTORYID ORDER BY t2.TICKETTIME) as t2
WHERE t1.CURRENTSTATUS_ANALYST not in('On-Hold','Waiting For Customer','Resolved','Closed') and t1.ticketnumber = '211135'
GROUP BY t1.TICKETNUMBER;
calculate difference between two times in two rows in sql
Calculate Time Difference Between Two Consecutive Rows
with SQL Server you can use those very usefull windowed functions LEAD and FIRST_VALUE :
select *
,[duration(sec)] = DATEDIFF(SECOND
,ticketTime
,LEAD(ticketTime,1,ticketTime)over(partition by ticketNumber order by ticketTime)
)
,[cumulative duration(sec)] = DATEDIFF( SECOND
, FIRST_VALUE(ticketTime)over(partition by ticketNumber order by ticketTime)
, ticketTime)
from (values
(1,cast('20211101 10:00:01' as datetime))
,(1,'20211101 10:00:33')
,(1,'20211101 10:01:59')
)T(ticketNumber,ticketTime)
ticketNumber
ticketTime
duration(sec)
cumulative duration(sec)
1
2021-11-01 10:00:01.000
32
0
1
2021-11-01 10:00:33.000
86
32
1
2021-11-01 10:01:59.000
0
118

Get the rows with the latest date for an ID [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
My table has multiple rows for each id, with a calculated score and the date the calculation was done. Simplifying, it looks like this:
id
calc_score
date
1
82
today
1
53
yesterday
1
92
last week
2
23
today
2
60
yesterday
2
73
last week
I need a query that returns only today's scores for each ID. I tried various combinations of group by and distinct on but didn't get very far.
What's the best way for doing this?
PG has several ways. A simple approach is window functions, row_number, rank, etc.
The following will answer the question in your title, to return the latest (last) row per id (by date), which is different than the body of your question.
WITH cte AS (
SELECT t.*
, row_number() OVER (PARTITION BY id ORDER BY date DESC) AS rn
FROM scores
)
SELECT * FROM cte
WHERE rn = 1
;
This just picks the (latest dated) first row for each id based on the date column descending. This also assumes the date column really contains date values or something orderable like a date.
To answer your question in the question body:
I need a query that returns only today's scores for each ID
more strictly, we could also do this:
SELECT *
FROM scores
WHERE date = current_date
;
If you want only today's scores, then you could use a direct comparison if the date has no time component:
where date = current_date
Or for either dates or timestamps:
where date >= current_date and date < current_date + interval '1 day'
If you want the most recent row per id, you would use distinct on:
select distinct on (id) t.*
from t
order by id, date desc;

How to carry over latest observed record when grouping by on SQL?

(I've created a similar question before, but I messed it up beyond repair. Hopefully, I can express myself better this time.)
I have a table containing records that change through time, each row representing a modification in Stage and Amount. I need to group these records by Day and Stage, summing up the Amount.
The tricky part is: ids might not change in some days. Since there won't be any record in those days, so I need to carry over the latest record observed.
Find below the records table and the expected result. MRE on dbfiddle (PostgreSQL)
Records
Expected Result
I created this basic visualization to demonstrate how the Amounts and Stages change throughout the days. Each number/color change represents a modification.
The logic behind the expected result can be found below.
Total Amount by Stage on Day 2
Id A was modified on Day 2, let's take that Amount: Negotiation 60.
Id B wasn't modified on Day 2, so we carry over the most recent modification (Day 1): Open 10.
Open 10
Negotiation 60
Closed 0
Total Amount by Stage on Day 3
Id A wasn't modified on Day 3, so we carry over the most recent modification (Day 2): Negotiation 60.
Id A was modified on Day 3: Negotiation 30
Total Amount by Stage on Day 3
Open 0
Negotiation 90
Closed 0
Basically, you seem to want the most recent value for each id --- and it only gets counted for the most recent stage.
You can get this using a formulation like this:
select d.DateDay, s.stage, coalesce(sh.amount, 0)
from (select distinct sh.DateDay from stage_history sh) d cross join
(select distinct sh.stage from stage_history sh) s left join lateral
(select sum(sh.amount) as amount
from (select distinct on (sh.id) sh.*
from stage_history sh
where sh.DateDay <= d.DateDay
order by sh.id, sh.DateDay desc
) sh
where sh.stage = s.stage
) sh
on 1=1
order by d.DateDay, s.stage;
Here is a db<>fiddle.

using datepart() with the group by command

I am beating my head against something that is, I'm sure, very obvious -
I have a bit of SQL code designed to sum the total selling price of each invoice in my store and then organize it by Month.
select SUM(totalsellingprice) from dbo.tblServiceOrders
where datepart(MONTH,dbo.tblServiceOrders.datereceived) =12
As far as I understand it, that should return the sum of all the totatlsellingprice from month 12 (December). Currently, this query returns
135998.92
However, if I then try to put that into a group by to get it to spit it out for all months, the number changes.
select SUM(totalsellingprice) from dbo.tblServiceOrders
group by datepart(MONTH,dbo.tblServiceOrders.datereceived)
And I get this table -
1 - 110567.70
2 - 60059.59
3 - 135998.92
4 - 63089.22
5 - 102287.01
6 - 71088.68
7 - 149102.10
8 - 67722.65
9 - 67122.45
10 - 64234.82
11 - 7542.05
12 - 130461.10
There are 12 rows, which sounds good to me (12 months in a year) but the last row is 130461.
How is it possible that row 12 from the second search does not equal what I did in the first search? I feel like I'm missing something obvious but I can't for the life of me figure out what.
Any and all help will be much appreciated!
I got it:
Your query is very confusing since it does not include the MONTH column:
If you would have done that, you would have realized your query is not ordered by MONTH and so, the MONTH 12 is returned as the 3rd row of your query.
;)
select SUM(totalsellingprice) from dbo.tblServiceOrders
group by datepart(MONTH,dbo.tblServiceOrders.datereceived)
order by datepart(MONTH,dbo.tblServiceOrders.datereceived)
And please, don't refer to the row index to choose which month is related to which sum. And should be a good idea to also discriminate the year (if you need to).
Run this and see what it does...
select dateadd(month, datediff(month, 0, datereceived), 0),
Sum(totalsellingprice)
from dbo.tblServiceOrders
group by dateadd(month, datediff(month, 0, datereceived), 0)