what is the difference between setting date condition with extract date and date between d1 and d2 in sql - sql

i have written two queries which i expected would give me the same data.
Query 1
select transaction, count(*)
from table
where create_date between to_Date('02/11/2017','MM/DD/YYYY') and to_date('02/17/2017','MM/DD/YYYY')
group by transaction
Query 2
select transaction, count(*)
from table
where extract(day from create_date) between 11 and 17
and extract(month from create_date)=2
and extract(year from create_date)=2017
group by transaction
Results from query 1
Transaction1 1155
Transaction2 333
Transaction3 5188
Results from query 2
Transaction1 1422
Transaction2 415
Transaction3 6155
why am i getting different results?

The first query gets the values where the values are between 2017-02-11 00:00:00 and 2017-02-17 00:00:00.
The second query gets the values where the values are between 2017-02-11 00:00:00 and 2017-02-17 23:59:59.
So, if there are values between 2017-02-17 00:00:01 and 2017-02-17 23:59:59 then they will be included in the COUNT of the second query but not the first.
Try:
select transaction, count(*)
from table
where create_date >= DATE '2017-02-11'
AND create_date < DATE '2017-02-18'
group by transaction
or
select transaction, count(*)
from table
where TRUNC( create_date ) BETWEEN DATE '2017-02-11' AND DATE '2017-02-18'
group by transaction
(Note: the later query will not use indexes on create_date and would need a function-based index on TRUNC( create_date ) instead.)

TO_DATE - Convert String to Datetime, and internally the between clause is working on HH: MI: SS in your first query , for making same result from second one need to take care about the HH: MI: SS in your second query

Related

PGSQL query to get a list of sequential dates from today

I am having an calendar table where I have added the list of dates on which no action should be performed.
The table is as follows and the date format is YYYY-MM-DD
date
2021-01-01
2021-04-05
2021-04-06
2021-04-07
2021-08-10
2021-11-22
2021-11-23
2021-11-24
2021-12-25
2021-12-31
Considering today is 2021-11-24.
The expected output is
date
2021-11-24
2021-11-23
2021-11-22
And Considering today is 2021-12-25
then the expected output is
date
2021-12-25
And Considering today is 2021-12-27
then the output should contain no data.
date
It should get me the sequence with today's date in descending order without a break of sequence.
I searched on various posts I did find some of the posts related to my question but the query was little complex with nested subqueries. Is there a way to achieve the output in a more optimized way. I am new to pgsql.
Create example table:
CREATE TABLE calendar (d date);
INSERT INTO calendar VALUES ('2021-11-23'),('2021-11-20');
Query:
SELECT * FROM
(SELECT CURRENT_DATE - '1 day'::interval * generate_series(0,10) AS d) a
LEFT JOIN calendar c ON (c.d=a.d);
a.d | c.d
---------------------+------------
2021-11-14 00:00:00 | Null
2021-11-15 00:00:00 | Null
2021-11-16 00:00:00 | Null
2021-11-17 00:00:00 | Null
2021-11-18 00:00:00 | Null
2021-11-19 00:00:00 | Null
2021-11-20 00:00:00 | 2021-11-20
2021-11-21 00:00:00 | Null
2021-11-22 00:00:00 | Null
2021-11-23 00:00:00 | 2021-11-23
2021-11-24 00:00:00 | Null
Subquery "a" generates a date series, and then we join it to the table.
You can add conditions , for example "WHERE calendar.d IS NULL", or "IS NOT NULL" depending on the filtering you want.
You can simply filter by a date range, building it by subtracting 2 days from today:
select "date"
from maintenance_dates_70099898
where "date" <= now()::date --you want to see today and 2 days prior; Last 3 days total
and "date" >= now()::date - '2 days'::interval
order by 1 desc;
With a runnable test:
drop table if exists maintenance_dates_70099898;
create table maintenance_dates_70099898 ("date" date);
insert into maintenance_dates_70099898
("date")
values
('2021-01-01'),
('2021-04-05'),
('2021-04-06'),
('2021-04-07'),
('2021-08-10'),
('2021-11-22'),
('2021-11-23'),
('2021-11-24'),
('2021-12-25'),
('2021-12-31');
select "date"
from maintenance_dates_70099898
where "date" <= now()::date --you want to see today and 2 days prior; Last 3 days total
and "date" >= now()::date - '2 days'::interval
order by 1 desc;
-- date
--------------
-- 2021-11-24
-- 2021-11-23
-- 2021-11-22
--(3 rows)
select "date"
from maintenance_dates_70099898
where "date" >= '2021-12-25'::date - '2 days'::interval
and "date" <= '2021-12-25'::date
order by 1 desc;
-- date
--------------
-- 2021-12-25
--(1 row)
I assume that for 2021-12-27 you do want to see 2021-12-25, as it's within the 3 day range prior.
select "date"
from maintenance_dates_70099898
where "date" >= '2021-12-28'::date - '2 days'::interval
and "date" <= '2021-12-28'::date
order by 1 desc;
-- date
--------
--(0 rows)
The main issue appears to be not having a known number of days thus disabling a simple range validation/selection. However to the rescue there is a RECURSIVE cte to pluck off each previous date that is exactly 1 day prior to the last and terminate when no longer holds.
with recursive no_action(no_act_dt) as
( select no_act_dt
from no_action_calendar
where no_act_dt = :parm_date::date
union all
select c.no_act_dt
from no_action_calendar c
join no_action a
on (c.no_act_dt = a.no_act_dt - 1)
)
select *
from no_action
order by no_act_dt desc;
If you use this often or from several points, you can parametrize it with a SQL function. (see demo for both).
create or replace
function consective_no_action_dates (date_in date)
returns setof date
language sql
as $$
with recursive no_action(no_act_dt) as
( select no_act_dt
from no_action_calendar
where no_act_dt = date_in
union all
select c.no_act_dt
from no_action_calendar c
join no_action a
on (c.no_act_dt = a.no_act_dt - 1)
)
select *
from no_action
order by no_act_dt desc;
$$;

SQL select dates from multi rows and datediff total hours

Hi have records entered into a table, I want to get the hours worked between rows.
id memberid dayname datesigned orderinout
310 987654321 Friday 2021-08-13 09:22:42 1
311 987654321 Friday 2021-08-13 10:15:50 2
312 987654321 Friday 2021-08-13 10:20:00 3
313 987654321 Friday 2021-08-13 12:36:15 4
314 987654321 Friday 2021-08-13 13:01:55 5
315 987654321 Friday 2021-08-13 18:55:41 6
Ideally I would like to work select a member and get the date signed, easy. then do a datediff to work out the hh:mm:ss difference. all good with 2 dates but multi on the same day? little stuck.
SELECT TIMEDIFF(MAX(datesigned),MIN(datesigned)) AS HoursIn
WHERE memberid = '987654321'
AND dayname = 'Friday'
when the date is saved, it will assign a number, first record will be 1 and so on for the member and the date.
so need to get the results for 1+2 then 3+4, 5+6 so on. might even be an odd one.
Any suggestions as im totally lost.
Use the LAG function to achieve the next record. Arrange the columns using orderinout and access the next row with the LAG function. 1 and 2 , 3 and 4 and .............
The TIMEDIFF function exists in mysql, and assuming your database management system is mysql, the following code.
in mysql
SELECT
id,
memberid,
dayname,
datesigned,
orderinout,
TIMEDIFF(datesigned,lag(datesigned,1) over(partition by memberid order by orderinout)) as HoursIn
from t
WHERE memberid = '987654321'
AND dayname = 'Friday'
demo in db<>fiddle
in sql-server
SELECT
id,
memberid,
dayname,
datesigned,
orderinout,
CONVERT (TIME, datesigned - lag(datesigned,1) over(partition by memberid order by orderinout)) as HoursIn
from t
WHERE memberid = '987654321'
AND dayname = 'Friday'
demo in db<>fiddle
If you want to calculate for all members and every day, use the LAG function as follows.
lag(datesigned,1) over(partition by memberid,dayname order by orderinout)
full query
SELECT
id,
memberid,
dayname,
datesigned,
orderinout,
TIMEDIFF(datesigned,lag(datesigned,1) over(partition by memberid,dayname order by orderinout)) as HoursIn
from t

Sum of item count in an SQL query based on DATE_TRUNC

I've got a table which contains event status data, similar to this:
ID Time Status
------ -------------------------- ------
357920 2019-12-25 09:31:38.854764 1
362247 2020-01-02 09:31:42.498483 1
362248 2020-01-02 09:31:46.166916 1
362249 2020-01-02 09:31:47.430933 1
362300 2020-01-03 09:31:46.932333 1
362301 2020-01-03 09:31:47.231288 1
I'd like to construct a query which returns the number of successful events each day, so:
Time Count
-------------------------- -----
2019-12-25 00:00:00.000000 1
2020-01-02 00:00:00.000000 3
2020-01-03 00:00:00.000000 2
I've stumbled across this SO answer to a similar question, but the answer there is for all the data returned by the query, whereas I need the sum grouped by date range.
Also, I cannot use BETWEEN to select a specific date range, since this query is for a Grafana dashboard, and the date range is determined by the dashboard's UI. I'm using Postgres for the SQL dialect, in case that matters.
You need to remove the time from time component. In most databases, you can do this by converting to a date:
select cast(time as date) as dte,
sum(case when status = 1 then 1 else 0 end) as num_successful
from t
group by cast(time as date)
order by dte;
This assumes that 1 means "successful".
The cast() does not work in all databases. Other alternatives are things like trunc(time), date_trunc('day', time), date_trunc(time, day) -- and no doubt many others.
In Postgres, I would phrase this as:
select date_trunc('day', time) as dte,
count(*) filter (where status = 1) as num_successful
from t
group by dte
order by dte;
How about like this:
SELECT date(Time), sum(status)
FROM table
GROUP BY date(Time)
ORDER BY min(Time)

Open Ticket Count Per Day

I have a table that looks like this
id | Submit_Date | Close_Date
------------------------------
1 | 2015-02-01 | 2015-02-05
2 | 2015-02-02 | 2015-02-04
3 | 2015-02-03 | 2015-02-05
4 | 2015-02-04 | 2015-02-06
5 | 2015-02-05 | 2015-02-07
6 | 2015-02-06 | 2015-02-07
7 | 2015-02-07 | 2015-02-08
I can get a count of how many ticket were open on a particular day with this:
Select count(*) from tickets where '2015-02-05' BETWEEN Submit_Date and Close_Date
This gives me 4, but I need this count for each day of a month. I don't want to have to write 30 queries to handle this. Is there a way to capture broken down by multiple days?
I created a solution a way back using a mix of #Heinzi s solution with the trick from Generate a resultset of incrementing dates in TSQL
declare #dt datetime, #dtEnd datetime
set #dt = getdate()
set #dtEnd = dateadd(day, 100, #dt)
SELECT dates.myDate,
(SELECT COUNT(*)
FROM tickets
WHERE myDate BETWEEN Submit_Date and Close_Date
)
FROM
(select Dates_To_Checkselect dateadd(day, number, #dt) mydate
from
(select distinct number from master.dbo.spt_values
where name is null
) n
where dateadd(day, number, #dt) < #dtEnd) dates
Code is combined from memory, I don't have it in front of me so there can be some typo's
First, you'll need a table that contains each date you want to check. You can use a temporary table for that. Let's assume that this table is called Dates_To_Check and has a field myDate:
SELECT myDate,
(SELECT COUNT(*)
FROM tickets
WHERE myDate BETWEEN Submit_Date and Close_Date)
FROM Dates_To_Check
Alternatively, you can create a huge table containing every possible date and use a WHERE clause to restrict the dates to those you are interested in.
If you're in SQL Server 2012 or newer you can do this using window functions with a small trick where you add 1 to the open days -1 to the closing days and then do a running total of this amount:
select distinct date, sum(opencnt) over (order by date) from (
select
Submit_Date as date,
1 as opencnt
from
ticket
union all
select
dateadd(day, 1, Close_Date),
-1
from
ticket
) TMP
There's a dateadd + 1 day to include the close date amount to that day
You could generate the list of dates and then retrieve the count for each date in your dateset.
The cte part generates the date list since the beginning of the year (an ssumption) and the next part calculates the count from your data set.
with cte as
(select cast('2015-01-01' as date) dt // you should change this part to the correct start date
union all
select dateadd(DD,1,dt) dt from cte
where dt<getdate()
)
select count(*)
from tickets
inner join cte
on cte.dt between Submit_Date and Close_Date
group by cte.dt

PostgreSQL query for multiple update

I have a table in which I have 4 columns: emp_no,desig_name,from_date and to_date:
emp_no desig_name from_date to_date
1001 engineer 2004-08-01 00:00:00
1001 sr.engineer 2010-08-01 00:00:00
1001 chief.engineer 2013-08-01 00:00:00
So my question is to update first row to_date column just one day before from_date of second row as well as for the second one aslo?
After update it should look like:
emp_no desig_name from_date to_date
1001 engineer 2004-08-01 00:00:00 2010-07-31 00:00:00
1001 sr.engineer 2010-08-01 00:00:00 2013-07-31 00:00:00
1001 chief.engineer 2013-08-01 00:00:00
You can calculate the "next" date using the lead() function.
This calculated value can then be used to update the table:
with calc as (
select promotion_id,
emp_no,
from_date,
lead(from_date) over (partition by emp_no order by from_date) as next_date
from emp
)
update emp
set to_date = c.next_date - interval '1' day
from calc c
where c.promotion_id = emp.promotion_id;
As you can see getting that value is quite easy, and storing derived information is very often not a good idea. You might want to consider a view that calculates this information on the fly so you don't need to update your table each time you insert a new row.
SQLFiddle example: http://sqlfiddle.com/#!15/31665/1