mysql select two prior to and one after NOW() - sql

I have a table with a column event_time. How can I select two rows right before NOW() and the next one after NOW(), ordered by event_time?
Is is possible with a single query?

How about something like
SELECT *
FROM (
SELECT *
FROM TABLE
WHERE event_time < NOW()
ORDER BY event_time DESC
LIMIT 2
) First2
UNION ALL
SELECT *
FROM (
SELECT *
FROM TABLE
WHERE event_time > NOW()
ORDER BY event_time ASC
LIMIT 1
) Next1
ORDER BY event_time

Related

Oracle SQL to delete table entries using having clause

I have a SQL query which finds userids in a table based on last login date
select USER_ID, max(login_time) maxd from MyTable group by User_Id having max(login_time) < (sysdate - 90)
I need to delete the entries that this query finds
I have tried
DELETE a
FROM MyTable a
JOIN
(
select USER_ID, max(login_time) maxd from MyTable group by User_Id having max(login_time) < (sysdate - 90)
) b ON a.USER_ID = b.USER_ID
but this results in
SQL Error: ORA-00933: SQL command not properly ended
Can anyone help ?
Oracle supports delete join syntax, but it is a bit different than other databases.
DELETE FROM (
SELECT a.*
FROM MyTable a
INNER JOIN
(
SELECT USER_ID
FROM MyTable
GROUP BY User_Id
HAVING MAX(login_time) < (sysdate - 90)
) b ON a.USER_ID = b.USER_ID
);
Use your select as a subquery:
DELETE FROM MyTable
WHERE (user_id, login_time) IN
-- this is your query:
( SELECT USER_ID, MAX (login_time) maxd
FROM MyTable
GROUP BY User_Id
HAVING MAX (login_time) < (SYSDATE - 90));
You can correlate on the ROWID pseudo-column and use an analytic function:
DELETE FROM mytable
WHERE ROWID IN (
SELECT ROWID
FROM (
SELECT login_time,
MAX(login_time) OVER (PARTITION BY user_id) AS max_login_time
FROM mytable
)
WHERE max_login_time < SYSDATE - INTERVAL '90' DAY
AND login_time = max_login_time
)

Postgresql: Gaps Between tsranges, empty set

I have a tables of reservations for each user:
reservations_development=# \d reservations
Table "public.reservations"
Column | Type | Modifiers
------------+---------+-----------------------------------------------------------
id | integer | not null default nextval('reservations_id_seq'::regclass)
user_id | integer |
occurrence | tsrange |
Indexes:
"reservations_pkey" PRIMARY KEY, btree (id)
"reservations_occurrence_user_id_excl" EXCLUDE USING gist (occurrence WITH &&, user_id WITH =)
I am trying to create a view of the gaps/opening between reservations for each user, and I currently have the following query:
CREATE OR REPLACE VIEW reservation_gaps AS (
with user_mins as (select tsrange(LOCALTIMESTAMP, min(lower(occurrence))), user_id
FROM (
SELECT user_id, occurrence
FROM reservations
WHERE lower(occurrence) >= LOCALTIMESTAMP
) as y
GROUP BY user_id
),
gaps as (select
tsrange(upper(occurrence), lead(lower(occurrence),1, LOCALTIMESTAMP + interval '1 year') over (win_user_gaps)),
user_id
from (
select user_id, occurrence
from reservations
) as x
WINDOW win_user_gaps AS (PARTITION BY user_id ORDER BY occurrence)
UNION ALL SELECT * FROM user_mins
)
select *
FROM gaps
ORDER BY user_id, tsrange
);
It currently gives the expected results as long as the user has one reservation, but if the user is new, and has not currently been reserved I get an empty result.
I need to in some way append a {tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + interval '1 year'), user_id} row to the view for each user without a reservation, but I'm currently stumped as to how to do that.
Thanks
You should change the CTE to be a UNION ALL with the artificial rows and then use DISTINCT ON to select one row per user.
with user_mins as (SELECT DISTINCT ON (user_id) user_id, tsrange FROM(
select tsrange(LOCALTIMESTAMP, min(lower(occurrence))) as tsrange, user_id, 1 as priotity
FROM (
SELECT user_id, occurrence
FROM reservations
WHERE lower(occurrence) >= LOCALTIMESTAMP
) as y
GROUP BY user_id
UNION ALL
SELECT user_id, tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + interval '1 year'),
0
FROM users)
ORDER BY user_id, priority DESC
)
SQL Fiddle
with this_year as (
select tsrange(
date_trunc('year', current_date)::timestamp,
date_trunc('year', current_date)::timestamp + interval '1' year, '[)'
) as this_year
), gaps as (
select
user_id,
this_year - tsrange(lower(occurrence), 'infinity', '[]') lower_range,
this_year - tsrange('-infinity', upper(occurrence), '[]') upper_range,
this_year
from
reservations
cross join
this_year
)
select *
from (
select
user_id,
upper_range *
lead (lower_range, 1, this_year)
over (partition by user_id order by lower_range, upper_range)
as gap
from gaps
union (
select distinct on (user_id)
user_id,
tsrange(
lower(this_year),
coalesce(upper(lower_range), upper(this_year)),
'[)'
) as gap
from gaps
order by user_id, lower_range
)
) s
where gap != 'empty'
order by user_id, gap

Filter rows by those created within a close timeframe

I have a application where users create orders that are stored in a Oracle database. I'm trying to find a bug that only happens when a user creates orders within 30 seconds of the last order they created.
Here is the structure of the order table:
order_id | user_id | creation_date
I would like to write a query that can give me a list of orders where the creation_date is within 30 seconds of the last order for the same user. The results will hopefully help me find the bug.
I tried using the Oracle LAG() function but it doesn't seem to with the WHERE clause.
Any thoughts?
SELECT O.*
FROM YourTable O
WHERE EXISTS (
SELECT *
FROM YourTable O2
WHERE
O.creation_date > O2.creation_date
AND O.user_id = O2.user_id
AND O.creation_date - (30 / 86400) <= O2.creation_date
);
See this in action in a Sql Fiddle.
You can use the LAG function if you want, you would just have to wrap the query into a derived table and then put your WHERE condition in the outer query.
SELECT distinct
t1.order_id, t1.user_id, t1.creation_date
FROM
YourTable t1
join YourTable t2
on t2.user_id = t1.user_id
and t2.creation_date between t1.creation_date - 30/86400 and t1.creation_date
and t2.rowid <> t1.rowid
order by 3 desc
Example of using LAG():
SELECT id, (pss - css) time_diff_in_seconds
, creation_date, prev_date
FROM
(
SELECT id, creation_date, prev_date
, EXTRACT(SECOND From creation_date) css
, EXTRACT(SECOND From prev_date) pss
FROM
(
SELECT id, creation_date
, LAG(creation_date, 1, creation_date) OVER (ORDER BY creation_date) prev_date
FROM
( -- Table/data --
SELECT 1 id, timestamp '2013-03-20 13:56:58' creation_date FROM dual
UNION ALL
SELECT 2, timestamp '2013-03-20 13:57:27' FROM dual
UNION ALL
SELECT 3, timestamp '2013-03-20 13:59:16' FROM dual
)))
--WHERE (pss - css) <= 30
/
ID TIME_DIFF_IN_SECONDS
--------------------------
1 0 <<-- if uncomment where
2 31
3 11 <<-- if uncomment where

Sql query needs to sort on multiple date columns together

I have a table with three date fields, a start date, mid-term date, and end date. I would like to create a single query to get the most recent activity from the table. Activity in this case being when the date fields are updated.
Without having to write 3 separate queries and then combining the values in my code to get the 10 most recent activities, can I do this in one query. So right now I have
SELECT TOP 10 * FROM core_table
ORDER BY [start_date] Desc
SELECT TOP 10 * FROM core_table
ORDER BY [process_date] Desc
SELECT TOP 10 * FROM core_table
ORDER BY [archive_date] Desc
So I would want to pull the results of those three queries together to get the top 10 entries based on all three dates.
based on answer given by Itiong_sh, which is not exactly the same : you can do it in ORDER BY
select top 10 * from core_table
order by
CASE
WHEN start_date >= process_date AND start_date >= archive_date
THEN start_date
WHEN process_date >= archive_date
THEN process_date
ELSE archive_date
END
DESC
I think you need UNION:
SELECT TOP 10
*
FROM
( ( SELECT TOP 10
*, start_date AS activity_date
FROM core_table
ORDER BY [start_date] DESC
)
UNION
( SELECT TOP 10
*, process_date AS activity_date
FROM core_table
ORDER BY [process_date] DESC
)
UNION
( SELECT TOP 10
*, archive_date AS activity_date
FROM core_table
ORDER BY [archive_date] DESC
)
) AS t
ORDER BY activity_date DESC ;
An expansion on Raphaƫl Althaus' answer:
CREATE TABLE core_table (
...
max_date AS
CASE
WHEN start_date >= process_date AND start_date >= archive_date
THEN start_date
WHEN process_date >= archive_date
THEN process_date
ELSE archive_date
END
);
CREATE INDEX core_table_ie1 ON core_table (max_date);
Then, you can simply...
SELECT TOP 10 *
FROM core_table
ORDER BY max_date DESC;
...and it should use the index range scan instead of a full table scan.

Get another column from sum-sub-select

I'm selecting something from a sub-select, which in turn gives me a list of sums. Now I want to select the base_unit column, which contains the unit of measurement. I can't seem to add base_unit to the sub-select because then it doesn't work with the GROUP BY statement.
SELECT to_char(a.pow * f_unit_converter(base_unit, '[W]'), '000.00')
FROM (
SELECT sum (time_value) AS pow
FROM v_value_quarter_hour
WHERE
mp_id IN (SELECT mp_id FROM t_mp WHERE mp_name = 'AC') AND
(now() - time_stamp < '5 day')
GROUP BY time_stamp
ORDER BY time_stamp DESC
) a
LIMIT 1
Where/how can I additionally select the base_unit from the t_mp Table for each of those sums, so that I can pass it to the f_unit_converter function?
Thanks a lot,
MrB
SELECT to_char(a.pow * f_unit_converter(a.base_unit, '[W]'), '000.00')
FROM (
SELECT sum (time_value) AS pow, t_mp.base_unit
FROM v_value_quarter_hour
inner join t_mp on (v_value_quarter_hour.mp_id = t_mp.mp_id)
WHERE
t_mp.mp_name = 'AC' AND
(now() - time_stamp < '5 day')
GROUP BY time_stamp, base_unit
ORDER BY time_stamp DESC
) a
LIMIT 1
Assuming that all your selected rows have the same base_unit, you should be able to add it both to the SELECT and the GROUP BY of your sub-query.
Use an INNER JOIN instead of an IN. Something like this
SELECT to_char(a.pow * f_unit_converter(base_unit, '[W]'), '000.00') FROM (
SELECT sum (time_value), base_unit AS pow
FROM v_value_quarter_hour
INNER JOIN t_mp ON v_value_quarter_hour.mp_id = t_mp.mp_id
WHERE mp_name = 'AC' AND
now() - time_stamp < '5 day'
GROUP BY time_stamp, base_unit
ORDER BY time_stamp DESC ) a LIMIT 1