I want to retrieve sum of weight data from a table over a whole month.
what I need help with is that I want to group the result into 2 parts
sum of 1-15 of the month
and second line 16-31 of the month.
SELECT(SUM(B.SCALE_WEIGHT) FROM TRACKING.DATALOG_TAB B WHERE B.MATERIALID= 1 AND B.SCALE_EVENTDATE BETWEEN TO_DATE(TRUNC(TO_DATE('2020-10-1', 'YYYY-MM-DD'),'MONTH')) AND TO_DATE(TRUNC(TO_DATE('2020-10-1', 'YYYY-MM-DD'), 'MONTH')+30)
GROUP BY(somthing like this - 1-15 and 16-31)
Here is one option:
select
1 + floor(extract(day from scale_eventdate) / 16) as fortnight,
sum(b.scale_weight) as sum_scale_weight
from tracking.datalog_tab b
where
materialid = 1
and scale_eventdate >= date '2020-10-01'
and scale_eventdate < date '2020-11-01'
group by 1 + floor(extract(day from scale_eventdate) / 16)
This extracts the day number from the date, and then use artithmetics: every day from the 1 to to the 15th of the month included goes to fortnight number 1, and everything afterwards goes to bucket 2.
We could also do this with to_char() and a case expression, which is somewhat more expressive:
select
case when to_char(scale_eventdate, 'dd') <= '15' then 1 else 2 end as fortnight,
sum(b.scale_weight) as sum_scale_weight
from tracking.datalog_tab b
where
materialid = 1
and scale_eventdate >= date '2020-10-01'
and scale_eventdate < date '2020-11-01'
group by case when to_char(scale_eventdate, 'dd') <= '15' then 1 else 2 end
Note that I changed the date filtering logic to use standard date literals, which makes the query shorter and more readable.
Related
Every month I have to fetch records for the previous month from a Db2 database. How can I write a Db2 query to fetch the last month of data without hard-coding the date range? For example, when run in December 2021, the query would return records dated between '2021-11-01' AND '2021-11-30', and those dates would change dynamically when I run the same query a month later.
It's easy to can precompute the date range in a cte and then use it in the main query. Assuming your table t has a ts column to filter by, you can do:
with
r as (
select
to_date(year(c) || '-' || month(c) || '-01' , 'YYYY-MM-DD') as e,
to_date(year(p) || '-' || month(p) || '-01' , 'YYYY-MM-DD') as b
from (
select current date as c, current date - 1 month as p from sysibm.sysdummy1
) x
)
select *
from t
cross join r
where t.ts >= r.b and t.ts < r.e
See example at db<>fiddle.
There are a few ways to describe the prior month as a date range in a Db2 SQL query. Some datetime SQL functions are not available on Db2 for z/OS, but even then you can still use date arithmetic and the LAST_DAY() function.
First day of last month: LAST_DAY(CURRENT DATE - 2 MONTHS) + 1 DAY
Last day of last month: LAST_DAY(CURRENT DATE - 1 MONTH)
First day of this month: LAST_DAY(CURRENT DATE - 1 MONTH) + 1 DAY
Inclusive-exclusive example (preferred approach):
SELECT ... WHERE someDateTimeColumn >= LAST_DAY(CURRENT DATE - 2 MONTHS) + 1 DAY
AND someDateTimeColumn < LAST_DAY(CURRENT DATE - 1 MONTH) + 1 DAY
Inclusive-inclusive example (calling the DATE() function will prevent implicit type conversion which could skip some some qualifying rows):
SELECT ... WHERE someDateTimeColumn >= LAST_DAY(CURRENT DATE - 2 MONTHS) + 1 DAY
AND DATE(someDateTimeColumn) <= LAST_DAY(CURRENT DATE - 1 MONTH)
If you're querying Db2 for LUW v11.1 or newer, you can also call the THIS_MONTH() function to get the first day of an input month.
First day of last month: THIS_MONTH(CURRENT DATE - 1 MONTH)
First day of this month: THIS_MONTH(CURRENT DATE)
Inclusive-exclusive example:
SELECT ... WHERE someDateTimeColumn >= THIS_MONTH(CURRENT DATE - 1 MONTH)
AND someDateTimeColumn < THIS_MONTH(CURRENT DATE)
Let's say I want to get the profit between two dates. Then I can do something like this:
SELECT SUM(Profit)
FROM Sales
WHERE date BETWEEN '2014-01-01' AND '2014-02-01' AND <other_filters>
I would then like to compare it to a previous period offset by a fixed amount. It could be written something like this to get it in two rows:
SELECT SUM(Profit)
FROM Sales
WHERE date BETWEEN '2014-01-01' AND '2014-02-01' AND <other_filters>
UNION ALL
SELECT SUM(Profit)
FROM Sales
WHERE date BETWEEN '2014-01-01' - INTERVAL 1 YEAR AND '2014-02-01' - INTERVAL 1 YEAR AND <other_filters>
Is there a way to do this without a union? I am looking for something like this:
SELECT
SELECT SUM(Profit),
???
FROM Sales
WHERE date BETWEEN '2014-01-01' AND '2014-02-01' AND <other_filters>
I think the tricky part here is how to 'un-do' the where filter for the offseted-time calculation.
You can use conditional aggregation and OR the range checks in the WHERE clause (unless they are subsequent in which case you can combine them directly of course).
SELECT sum(CASE
WHEN date >= '2014-01-01'
AND date < '2014-02-02' THEN
profit
ELSE
0
END),
sum(CASE
WHEN date >= '2014-01-01' - INTERVAL 1 YEAR
AND date < '2014-02-02' - INTERVAL 1 YEAR THEN
profit
ELSE
0
END)
FROM sales
WHERE date >= '2014-01-01'
AND date < '2014-02-02'
OR date >= '2014-01-01' - INTERVAL 1 YEAR
AND date < '2014-02-02' - INTERVAL 1 YEAR;
Note: Prefer not to use BETWEEN here but check for a right half open range check. That way, if the precision of date changes, records on the end past midnight are still in the results.
I have a max date in my query and If the max date is current month then I want always get previous month’s date. How can I do that?
For example, today is 20160825. If the max date is 20160801 then I want to get 20160701. But, if the max date is 20160501 then I just want to get without changes - 20160501.
SELECT
DEFN_DK,
MAX(SNAPSHOT_MTH)
FROM myTable
WHERE TOT_AMT >0
GROUP BY DEFN_DK
Since your SNAPSHOT_MTH column is an integer (why?) you can not use any of the otherwise very useful timestamp functions. So it's back to integer math, creating a "month" from your snapshot "date" through integer division by 100. This can be compared to CURRENT_DATE by converting that to a string and then casting it to an integer. Going back 1 month similarly requires some math. Not entirely efficient, but here goes:
SELECT DEFN_DK,
CASE max(SNAPSHOT_MTH) / 100
WHEN tochar(CURRENT_DATE, 'YYYYMM')::int THEN
((max(SNAPSHOT_MTH) / 100) - 1) * 100 + 1
-- or max(SNAPSHOT_MTH) - 100, if you know it always ends in 01
ELSE max(SNAPSHOT_MTH)
END AS SNAPSHOT_MTH
FROM myTable
WHERE TOT_AMT > 0
GROUP BY DEFN_DK;
select
defn_dk,
case max(snapshot_mth)
when date_trunc('month', current_date) then max(snapshot_mth) - interval '1 month'
else max(snapshot_mth)
end
from mytable
where tot_amt >0
group by defn_dk
show this, two examples:
select
case when date_trunc('month',dd) = date_trunc('month',now())
then dd - interval'1 month' else date_trunc('day',dd) end
from
(
select '2016-08-04'::date as dd
) d;
and
select
case when date_trunc('month',dd) = date_trunc('month',now())
then dd - interval'1 month' else date_trunc('day',dd) end
from
(
select '2016-05-04'::date as dd
) d;
This is what I am using right now and it returns the number of days between beginDate and endDate
date( ' $(#endDate)') - date('$(#beginDate)') as weekNumber
How can I get it to return the number of weeks between the two?
The simplest way would be to divide the number of days by seven, although that would not take into account the starting day of the week.
You could use this instead:
extract (week from date '$(#endDate)') -extract (week from date '$(#startDate)')
which would use iso8601 week numbers. But beware of spanning years!
Version 1:
/*
Design:
Week begins on day of start date
Options:
1) Count only whole weeks
2) Count partial weeks on the right side (end date side)
*/
select
sum(case when ('2013-02-08'::date - ind::date) >= 7 then 1 else 0 end) as whole_weeks,
count(*) as partial_right
from
generate_series('2013-01-01'::date /*dow=2*/,'2013-02-05'::date /*dow=2*/,'7 days') ind
Version 2:
/*
Design:
Week begins on specific day of week (5 chosen in this example)
Options:
1) Count only whole weeks
2) Count partial weeks on the right side (end date side)
3) Count partial weeks on the left side (start date side)
4) Count partial weeks on both sides
*/
select
sum(case when days = 7 then 1 else 0 end) as whole_weeks,
sum(case when days = 7 or max_ind = week_start then 1 else 0 end) as partial_right,
sum(case when days = 7 or week_start < min_ind then 1 else 0 end) as partial_left,
count(*) as partial_both_sides
from
(
select
ind - (case when dow < bow then dow + 7 - bow else dow - bow end)::int as week_start,
count(*) as days,
min(ind) as min_ind,
max(ind) as max_ind
from
(select
ind::date as ind,
extract(isodow from ind) as dow,
5::int as bow
from
generate_series('2013-01-01'::date /*dow=2*/,'2013-02-08'::date /*dow=5*/,'1 day') ind
) inp
group by
week_start
) t
I have a tabel in a relation database which contains a lot of dates.
I my application logic I have divided one day into 4 parts of 6 hours each, starting at: 00:00, 06:00, 12:00 and 18:00.
Now I would like to find the time difference of the earliest record in the database for each quater of a day, and the beginning og the peiod. How can I do that?
In psuedo-sql i guess it looks like
select min(created_at - ROUND_DOWN_TO_6_HOURS(created_at)) from mytabel group by day_quater;
The problem is how to calculate "ROUND_DOWN_TO_6_HOURS". So if "created_at" is 19:15 it will be rounded down to 18:00 and "created_at - ROUND_DOWN_TO_6_HOURS(created_at)" will return 1:15 hourd
I'm working with psql
If you're just trying to locate the records that match these ranges, you could just use that in the WHERE clause like
select * from myTable
where datepart(hh, created_at) between 0 and 6
If your trying to create a computed field that will have the 00 or 06 ... then you could use the "DatePart()" function in sql to pull the hour... DATEPART ( hh, date )... This would return a numeric value of 0, 1, 2, 3, ... 23 and you can compute a field based on this value being between 2 of your hours listed...
Here's a sample...
select
case
when datepart(hh, add_dt) between 0 and 6 then 1
when datepart(hh, add_dt) between 7 and 12 then 2
when datepart(hh, add_dt) between 13 and 18 then 3
when datepart(hh, add_dt) between 19 and 24 then 4
end
from myTable
where add_dt is not null
You could use CASE in conjunction with your date column and datetime functions to establish the quarter-of-day (1,2,3,4) and extract the day part from the datetime value, group by day, quarter, and then use the MIN(yourdatecolumn) to grab the earliest time within each quarter grouping.
Not sure what you mean by "beginning of the period". but you can measure the difference between any arbitrary datetime and your set of earliest times per day-quarter which was instantiated in the manner above.
http://www.postgresql.org/docs/8.2/static/functions-datetime.html
select
record::time - (case
when record::time >= '18:00' then '18:00'
when record::time >= '12:00' then '12:00'
when record::time >= '6:00' then '6:00'
else '0:00' end
)::time as difference
from my_table
My PostgreSQL is a little rusty, but something like this:
select
date_trunc('day',CreatedOn) [Day],
min(case when date_part('hour',TIMESTAMP CreatedOn) < 6 then '00:00'
when date_part('hour',TIMESTAMP CreatedOn) < 12 then '06:00'
when date_part('hour',TIMESTAMP CreatedOn) < 18 then '12:00'
else '18:00'
end) [Quarter]
from MyTable
group by date_trunc('day',CreatedOn)
order by date_trunc('day',CreatedOn)