I have a DateTime dimension with dates like this:
Datetime Date hour
--------------------------------------
2026-01-01 00:00:00.0000000 24
2025-12-31 23:00:00.0000000 23
2025-12-31 22:00:00.0000000 22
2025-12-31 21:00:00.0000000 21
2025-12-31 20:00:00.0000000 20
2025-12-31 19:00:00.0000000 19
2025-12-31 18:00:00.0000000 18
2025-12-31 17:00:00.0000000 17
2025-12-31 16:00:00.0000000 16
2025-12-31 15:00:00.0000000 15
2025-12-31 14:00:00.0000000 14
2025-12-31 13:00:00.0000000 13
2025-12-31 12:00:00.0000000 12
2025-12-31 11:00:00.0000000 11
2025-12-31 10:00:00.0000000 10
2025-12-31 09:00:00.0000000 9
2025-12-31 08:00:00.0000000 8
2025-12-31 07:00:00.0000000 7
2025-12-31 06:00:00.0000000 6
2025-12-31 05:00:00.0000000 5
2025-12-31 04:00:00.0000000 4
2025-12-31 03:00:00.0000000 3
2025-12-31 02:00:00.0000000 2
2025-12-31 01:00:00.0000000 1
Due to systemtime i need that my hours start at 6 o'clock instead, but how do i do that? I could create a temptable with hardcoded values and join on, but theres gotta be a simpler way?
Desired result:
Datetime Date hour
--------------------------------------
2026-01-01 00:00:00.0000000 19
2025-12-31 23:00:00.0000000 18
2025-12-31 22:00:00.0000000 17
2025-12-31 21:00:00.0000000 16
2025-12-31 20:00:00.0000000 15
2025-12-31 19:00:00.0000000 14
2025-12-31 18:00:00.0000000 13
2025-12-31 17:00:00.0000000 12
2025-12-31 16:00:00.0000000 11
2025-12-31 15:00:00.0000000 10
2025-12-31 14:00:00.0000000 9
2025-12-31 13:00:00.0000000 8
2025-12-31 12:00:00.0000000 7
2025-12-31 11:00:00.0000000 6
2025-12-31 10:00:00.0000000 5
2025-12-31 09:00:00.0000000 4
2025-12-31 08:00:00.0000000 3
2025-12-31 07:00:00.0000000 2
2025-12-31 06:00:00.0000000 1
2025-12-31 05:00:00.0000000 24
2025-12-31 04:00:00.0000000 23
2025-12-31 03:00:00.0000000 22
2025-12-31 02:00:00.0000000 21
2025-12-31 01:00:00.0000000 20
And for any questions about why I dont hold a DateDimension and a
TimeDimension. This cannot forefill my requirements since I have many
more business rules on my datetime dimensions
At a guess...
UPDATE YourTable
SET [Date hour] = IIF(DATEPART(HOUR,DATEADD(HOUR, -5, [datetime])) = 0,24,DATEPART(HOUR,DATEADD(HOUR, -5, [datetime]));
Given that your [Date hour] column is already computed, you may try updating using the modulus:
UPDATE yourTable
SET [Date hour] = 1 + ([Date hour] + 18) % 24;
Demo
A better long term solution might be to make your date hour column a computed column, based on the original date time information. Se the answer by #Larnu for one way to do this.
Related
I have a table in PostgreSQL
time
goals
assists
2022-03-01 00:00:00
22
1
2022-03-03 00:00:00
24
2
2022-03-07 00:00:00
25
3
I want to add missing timestamps and fill the goals and assists in, as displayed in the table below
time
goals
assists
2022-03-01 00:00:00
22
1
2022-03-02 00:00:00
22
1
2022-03-03 00:00:00
24
2
2022-03-04 00:00:00
24
2
2022-03-05 00:00:00
24
2
2022-03-06 00:00:00
24
2
2022-03-07 00:00:00
25
3
I have a calendar table where working days are marked.
Now I need a running total called "current_working_day" which sums up the working days until the end of a month and restarts again.
This is my query:
select
WDAYS.Date,
WDAYS.DayName,
WDAYS.WorkingDay,
sum(WDAYS.WorkingDay) OVER(order by (Date), MONTH(Date), YEAR(Date)) as 'current_working_day',
sum(WDAYS.WorkingDay) OVER(PARTITION by YEAR(WDAYS.Date), MONTH(WDAYS.Date) ) total_working_days_per_month
from WDAYS
where YEAR(WDAYS.Date) = 2022
This is my current output
Date
DayName
WorkingDay
current_working_day
total_working_days_per_month
2022-01-27
Thursday
1
19
21
2022-01-28
Friday
1
20
21
2022-01-29
Saturday
0
20
21
2022-01-30
Sunday
0
20
21
2022-01-31
Monday
1
21
21
2022-02-01
Tuesday
1
22
20
2022-02-02
Wednesday
1
23
20
2022-02-03
Thursday
1
24
20
But the column "current_workind_day" should be like this
Date
DayName
WorkingDay
current_working_day
total_working_days_per_month
2022-01-27
Thursday
1
19
21
2022-01-28
Friday
1
20
21
2022-01-29
Saturday
0
20
21
2022-01-30
Sunday
0
20
21
2022-01-31
Monday
1
21
21
2022-02-01
Tuesday
1
1
20
2022-02-02
Wednesday
1
2
20
2022-02-03
Thursday
1
3
20
Thanks for any advice.
You can try to use PARTITION by with EOMONTH function which might get the same result but better performance, then you might only need to order by Date instead of using the function with the date.
select
WDAYS.Date,
WDAYS.DayName,
WDAYS.WorkingDay,
sum(WDAYS.WorkingDay) OVER(PARTITION by EOMONTH(WDAYS.Date) order by Date) as 'current_working_day',
sum(WDAYS.WorkingDay) OVER(PARTITION by EOMONTH(WDAYS.Date) ) total_working_days_per_month
from WDAYS
where YEAR(WDAYS.Date) = 2022
I'm doing a basic example of recursive query with oracle sql. I'm computing future months of the format MON-YY. I managed to have a seemingly correct query but I don't understand the break condition with a WITH query.
I'm trying to break on the year value (for example stop when you reach 2020), but it detects a cycle while doing that. If I break on the month value (e.g. December), it works.
Here's my query with a month based break:
with
prochains_mois(mois, annee) as (
select 'sep' as mois, 19 as annee
from dual
union all
select
case mois
when 'jan' then 'fev'
when 'fev' then 'mar'
when 'mar' then 'avr'
when 'avr' then 'mai'
when 'mai' then 'jun'
when 'jun' then 'jui'
when 'jui' then 'aou'
when 'aou' then 'sep'
when 'sep' then 'oct'
when 'oct' then 'nov'
when 'nov' then 'dec'
when 'dec' then 'jan'
end,
case mois
when 'dec' then annee + 1
else annee
end
from prochains_mois r
where mois <> 'dec'
)
select * from prochains_mois;
If I do this, it returns a consistent result.
MOI ANNEE
--- ----------
sep 19
oct 19
nov 19
dec 19
Now if I try to break the recursive query on the year, let's say 2020, so I change the where condition in the with clause to :
where annee < 20
Then I get :
ORA-32044: cycle detected while executing recursive WITH query
I tried to break with a later month to see if my year addition works correctly, it seems to be the case. If I break on march, I get the January and February correctly :
where mois <> 'mar'
gives
MOI ANNEE
--- ----------
sep 19
oct 19
nov 19
dec 19
jan 20
fev 20
mar 20
Use DATEs:
with prochains_mois( value ) as (
select DATE '2019-09-01' from dual
union all
select ADD_MONTHS( value, 1 )
FROM prochains_mois
WHERE value < DATE '2020-12-01'
)
select SUBSTR( TO_CHAR( value, 'mon', 'NLS_DATE_LANGUAGE=FRENCH' ), 1, 3 ) AS mois,
TO_CHAR( value, 'RR' ) AS annee
from prochains_mois;
Output:
MOIS | ANNEE
:--- | :----
sep | 19
oct | 19
nov | 19
dec | 19
jan | 20
fev | 20
mar | 20
avr | 20
mai | 20
jui | 20
jui | 20
aou | 20
sep | 20
oct | 20
nov | 20
dec | 20
or use your query and check that the month and year do not match:
with
prochains_mois(mois, annee) as (
select 'sep' as mois, 19 as annee
from dual
union all
select
case mois
when 'jan' then 'fev'
when 'fev' then 'mar'
when 'mar' then 'avr'
when 'avr' then 'mai'
when 'mai' then 'jun'
when 'jun' then 'jui'
when 'jui' then 'aou'
when 'aou' then 'sep'
when 'sep' then 'oct'
when 'oct' then 'nov'
when 'nov' then 'dec'
when 'dec' then 'jan'
end,
case mois
when 'dec' then annee + 1
else annee
end
from prochains_mois r
where ( mois, annee ) NOT IN ( ( 'dec', 20 ) )
)
select * from prochains_mois;
Output:
MOIS | ANNEE
:--- | ----:
sep | 19
oct | 19
nov | 19
dec | 19
jan | 20
fev | 20
mar | 20
avr | 20
mai | 20
jun | 20
jui | 20
aou | 20
sep | 20
oct | 20
nov | 20
dec | 20
db<>fiddle here
Your main issue is that you're trying to manipulate dates using strings/numbers. Don't do that; if you're working with dates, use dates!
E.g. you can do what you're after like so:
WITH prochains_mois (mnth_dt) AS (SELECT TRUNC(sysdate, 'mm') mnth_dt
FROM dual
UNION ALL
SELECT add_months(mnth_dt, 1) mnth_dt
FROM prochains_mois
WHERE add_months(mnth_dt, 1) < add_months(TRUNC(sysdate, 'yyyy'), 12))
SELECT mnth_dt,
to_char(mnth_dt, 'mon') mois,
to_char(mnth_dt, 'yy') annee
FROM prochains_mois;
MNTH_DT MOIS ANNEE
----------- ---- -----
01/09/2019 sep 19
01/10/2019 oct 19
01/11/2019 nov 19
01/12/2019 dec 19
N.B. You could simplify the predicate in the recursive sub-factored query to mnth_dt < add_months(TRUNC(SYSDATE, 'yyyy'), 11).
This works by taking the start date (here, I've used sysdate) and finding the first of the month (by using the optional second parameter of TRUNC to specify the level we're truncating it to).
Then we simply add a month to each date until we hit the last month of the start date's year.
Only after you've got the dates do you then output the data in the format you require using to_char.
I have this sub query that needs to return the SUM(qty) for each rack and bin ordering the rack/bin by the earliest date first (ASC). Every time I use the date field to sort the data, it returns a record for every date. I added the date field in the SELECT statement for reference. How would I go about doing this??
SELECT InventoryItems_1.ItemID,
SUM(ISNULL(InventoryItems_1.QtyToStock, 0)) AS InvQty,
InventoryItems_1.Rack,
InventoryItems_1.Bin,
InventoryItems_1.LocationID,
MIN(InventoryItems_1.Date),
Locations_1.LocationCode,
Locations_1.DescriptionMed
FROM dbo.InventoryItems AS InventoryItems_1
INNER JOIN dbo.Locations AS Locations_1 ON InventoryItems_1.LocationID = Locations_1.LocationID
WHERE(InventoryItems_1.OwnerDetailID IS NULL)
GROUP BY InventoryItems_1.ItemID,
InventoryItems_1.LocationID,
InventoryItems_1.Rack,
InventoryItems_1.Bin,
InventoryItems_1.Date,
Locations_1.LocationCode,
Locations_1.DescriptionMed
HAVING InventoryItems_1.ItemID = 10308
ORDER BY InventoryItems_1.LocationID,
InventoryItems_1.Date,
InventoryItems_1.Rack,
InventoryItems_1.Bin;
This is my result:
ID Qty Rack Bin Loc Date LocID
10308 35 21 02-Z 7 2018-10-22 14:48:33.000 WI
10308 52.5 21 02-Z 7 2018-10-23 08:18:44.000 WI
10308 87.5 18 01-Z 7 2018-10-23 12:19:09.000 WI
10308 87.5 23 01-B 7 2018-10-24 11:02:35.000 WI
10308 35 19 09-Z 7 2018-12-06 14:24:14.000 WI
10308 22.5 19 09-Z 7 2018-12-06 16:52:26.000 WI
10308 30 19 09-Z 7 2018-12-07 07:55:59.000 WI
10308 55 19 09-Z 7 2018-12-07 08:54:55.000 WI
10308 32.5 19 09-Z 7 2018-12-07 09:47:19.000 WI
10308 87.5 19 03-C 7 2018-12-07 11:36:20.000 WI
10308 72.5 19 10-Z 7 2018-12-07 13:17:03.000 WI
10308 15 19 10-Z 7 2018-12-07 14:30:38.000 WI
10308 32.5 18 07-A 7 2018-12-17 13:39:39.000 WI
10308 12.5 19 03-A 7 2018-12-17 14:48:57.000 WI
10308 42.5 19 03-A 7 2018-12-18 08:07:42.000 WI
10308 87.5 19 11-Z 7 2018-12-18 10:11:23.000 WI
10308 87.5 19 06-B 7 2018-12-18 12:08:17.000 WI
10308 87.5 18 03-Z 7 2018-12-26 13:40:34.000 WI
10308 55 21 05-Z 7 2018-12-26 14:48:58.000 WI
10308 32.5 21 05-Z 7 2018-12-27 07:49:27.000 WI
10308 87.5 19 01-B 7 2018-12-27 09:55:59.000 WI
10308 8 18 07-A 7 2018-12-28 09:40:11.000 WI
10308 0.5 18 08-B 7 2018-12-28 09:40:11.000 WI
10308 75.5 9 2018-11-27 11:55:17.000 NJ
10308 7 10 2018-10-24 08:28:26.000 TX
10308 2.5 10 2018-11-02 10:07:27.000 TX
10308 12.5 10 2018-11-02 14:36:57.000 TX
10308 10.5 10 2018-11-27 13:56:11.000 TX
This is what I want it to look like sorted by the oldest inventory date first.
ItemID InvQty Rack Bin Loc LocCode
10308 87.5 18 01-Z 7 WI
10308 87.5 18 03-Z 7 WI
10308 40.5 18 07-A 7 WI
10308 0.5 18 08-B 7 WI
10308 87.5 19 01-B 7 WI
10308 55 19 03-A 7 WI
10308 87.5 19 03-C 7 WI
10308 87.5 19 06-B 7 WI
10308 175 19 09-Z 7 WI
10308 87.5 19 10-Z 7 WI
10308 87.5 19 11-Z 7 WI
10308 87.5 21 02-Z 7 WI
10308 87.5 21 05-Z 7 WI
10308 87.5 23 01-B 7 WI
10308 75.5 9 NJ
10308 32.5 10 TX
You are grouping by the InventoryItems_1.Date field, which is why separate rows are coming out for each date. Changing your query to NOT group by this, and naming your minimum date field would allow you to generate the correct output. Also, if you want to limit your report to only one ID, put that in the WHERE clause, not the HAVING clause. This will filter the records before you perform your GROUP BY calculations etc, speeding your output.
SELECT InventoryItems_1.ItemID,
SUM(ISNULL(InventoryItems_1.QtyToStock, 0)) AS InvQty,
InventoryItems_1.Rack,
InventoryItems_1.Bin,
InventoryItems_1.LocationID,
MIN(InventoryItems_1.Date) As MinDate,
Locations_1.LocationCode,
Locations_1.DescriptionMed
FROM dbo.InventoryItems AS InventoryItems_1
INNER JOIN dbo.Locations AS Locations_1
ON InventoryItems_1.LocationID = Locations_1.LocationID
WHERE InventoryItems_1.OwnerDetailID IS NULL
AND InventoryItems_1.ItemID = 10308
GROUP BY InventoryItems_1.ItemID,
InventoryItems_1.LocationID,
InventoryItems_1.Rack,
InventoryItems_1.Bin,
Locations_1.LocationCode,
Locations_1.DescriptionMed
-- HAVING InventoryItems_1.ItemID = 10308
ORDER BY InventoryItems_1.LocationID,
MIN(InventoryItems_1.Date),
InventoryItems_1.Rack,
InventoryItems_1.Bin;
You can do it like this :
SELECT
InventoryItems_1.ItemID
, ISNULL(InventoryItems_1.QtyToStock, 0) AS InvQty
, InventoryItems_1.Rack
, InventoryItems_1.Bin
, InventoryItems_1.LocationID
, InventoryItems_1.Date
, Locations_1.LocationCode
, Locations_1.DescriptionMed
FROM (
SELECT *, ROW_NUMBER() OVER(PARTITION BY ItemID, Bin, LocationID ORDER BY [Date] DESC) RN
FROM dbo.InventoryItems
) InventoryItems_1
INNER JOIN dbo.Locations AS Locations_1 ON InventoryItems_1.LocationID = Locations_1.LocationID
WHERE
InventoryItems_1.RN = 1
AND InventoryItems_1.OwnerDetailID IS NULL
AND InventoryItems_1.ItemID = 10308
ORDER BY InventoryItems_1.LocationID,
InventoryItems_1.Rack,
InventoryItems_1.Bin;
I am trying to to extend the valid_till date for a month of tenants who have reference id more than two times.
refid, referrer_id, referrer_bonus_amount, referral_valid, valid_from, valid_till
1 2 2500 1 2015-07-05 2015-09-05
2 3 2500 1 2015-07-05 2015-09-05
3 5 1000 0 2015-12-13 2016-02-13
4 6 2500 0 2016-04-25 2016-06-24
5 10 1000 1 2015-07-01 2015-09-01
6 12 2500 1 2015-05-12 2015-07-12
7 13 2500 0 2015-08-05 2015-10-05
8 20 1000 1 2016-02-05 2016-04-05
9 2 2500 0 2015-08-12 2015-09-12
10 5 91000 1 2016-02-18 2016-04-18
11 20 1500 1 2016-06-19 2016-08-19
12 9 2500 0 2015-11-15 2016-01-15
13 13 91000 1 2016-02-01 2016-04-01
14 5 1000 1 2016-04-25 2016-06-24
To update the table (t) to add 1 month to the valid_till date for those refid that appear in referrer_id more than two times using exists() with having count(*) > 2:
update t
set valid_till = dateadd(month,1,valid_till)
output inserted.*
from t
where exists (
select 1
from t as i
where i.referrer_id = t.refid
group by referrer_id
having count(*) > 2
)
rextester demo: http://rextester.com/WXZC31875
output:
+-------+-------------+-----------------------+----------------+------------+------------+
| refid | referrer_id | referrer_bonus_amount | referral_valid | valid_from | valid_till |
+-------+-------------+-----------------------+----------------+------------+------------+
| 5 | 10 | 1000 | 1 | 2015-07-01 | 2015-10-01 |
+-------+-------------+-----------------------+----------------+------------+------------+