Oracle SQL query about Date - sql

I have a database table named availableTimeslot with fields pk, startDate, endDate, e.g.
PK startDate endDate
1. 2017-03-07 09:00:00 2017-03-07 18:00:00
2. 2017-03-07 18:00:00 2017-03-07 21:00:00
3. 2017-03-08 09:00:00 2017-03-08 18:00:00
records starting from 09:00:00 to 18:00:00 indicate it is a morning time slot, while 18:00:00 to 23:00:00 indicating it is a afternoon time slot
storing available timeslot dates (e.g. 2017-03-06, 2017-03-08) which are available for the customer to choose one.
Can I use one query to get exactly 10 available time slots dates starting on the day after the order date?
e.g. if I order a product on 2016-03-07, then the query returns
2017-03-08 09:00:00
2017-03-08 18:00:00
2017-03-09 09:00:00
2017-03-09 18:00:00
2017-03-10 ...
2017-03-11 ...
2017-03-13 ...
as 12 is a public holiday and not in the table.
In short, it returns 10 dates (5 days with each day having am and pm sessions)
remark: the available time slot dates are in order, but may not be consecutive

select available_date
from ( select available_date, row_number() over (order by available_date) as rn
from your_table
where available_date > :order_date
)
where rn <= 5;
:order_date is a bind variable - the date entered by the user/customer through the interface.

Do you want 5 for a single customer?
select ts.*
from (select ts.*
from customer c join
timeslots ts
on ts.date > c.orderdate
where c.customerid = v_customerid
order by ts.date asc
) ts
where rownum <= 5

Related

How to average values in one table based on the condition involving another table in SQL?

I have two tables. One defines time intervals (beginning and end). Time intervals are not equal in length. Another contains product ID, start and end date of the product.
TableOne:
Interval StartDateTime EndDateTime
202020201 2020-01-01 00:00:00 2020-02-10 00:00:00
202020202 2020-02-10 00:00:00 2020-02-20 00:00:00
TableTwo
ProductID ProductStartDateTime ProductEndDateTime
ASSDWE1 2018-01-04 00:12:00 2020-04-10 20:00:30
ADFGHER 2020-01-05 00:11:30 2020-01-19 00:00:00
ASDFVBN 2017-10-10 00:12:10 2020-02-23 00:23:23
I need to compute the average length of the products from TableTwo that existed during time intervals defined in TableOne. If the product existed throughout the time interval from TableOne, then the length of the product during this time interval is defined as it length since its start date till the end of the time interval.
I tried the following
select
a.*,
(select
AVG(datediff(day, b.ProductStartDateTime, IIF (b.ProductEndDateTime> a.EndDateTime, a.EndDateTime
,b.ProductEndDateTime))) --compute average length of the products
FROM #TableTwo b
WHERE ( not (b.ProductEndDateTime <= a.StartDateTime ) and not (b.ProductStartDateTime >= a.EndDateTime) )
-- select products that existed during interval from #TableOne
) as AverageProductLength
from #TableOne a
I get the mistake "Multiple columns are specified in an aggregated expression containing an outer reference. If an expression being aggregated contains an outer reference, then that outer reference must be the only column referenced in the expression."
The result I want:
Interval StartDateTime EndDateTime AverageProductLength
202020201 2020-01-01 00:00:00 2020-02-10 00:00:00 23
202020202 2020-02-10 00:00:00 2020-02-20 00:00:00 34.5
Is there a way I can do the averaging?

How can I extract the values of the last aggregation date in sql

I have the following table.
id user time_stamp
1 Mike 2020-02-13 00:00:00 UTC
2 John 2020-02-13 00:00:00 UTC
3 Levy 2020-02-12 00:00:00 UTC
4 Sam 2020-02-12 00:00:00 UTC
5 Frodo 2020-02-11 00:00:00 UTC
Let's say 2020-02-13 00:00:00 UTC is the last day and I would like to query this table to only display last days results? I want to create a view in Bigquery so that I only and always get the last day's results?
So that in the end I get something like this (For last day which is 2020-02-13 00:00:00 UTC )
id user time_stamp
1 Mike 2020-02-13 00:00:00 UTC
2 John 2020-02-13 00:00:00 UTC
You can use window functions:
select t.* except (seqnum)
from (select t.*,
dense_rank() over (order by time_stamp) as seqnum
from t
) t
where seqnum = 1;
This may not work well on a large amount of data -- because of the way that BQ implements window functions with no partitioning. So, you might find that this works better (especially if the above runs out of resources):
select t.*
from t join
(select max(time_stamp) as max_time_stamp
from t
) tt
on t.time_stamp = max_time_stamp;
Also, if the timestamps actually have date components, then you will want to convert to a date or remove the time component somehow.

Select to search column on group by query

I have one table called prices that have a reference from table products through product_id column. I want a query that selects prices grouped by product_id with the max final date and get the value of start_date through one select with id of price grouped.
I try with the following query but I am getting a wrong value of start date. Is weird because of the result subquery return more than one row even though I use the price id on where clause. Because that I put the limit on the query but it is wrong.
select prices.produto_id, prices.id,
MAX(CASE WHEN prices.finish_date IS NULL THEN COALESCE(prices.finish_date,'9999-12-31') ELSE prices.finish_date END) as finish_date,
(select start_date from prices where prices.id = prices.id limit 1)
as start_date from prices group by prices.product_id, prices.id
How I can get the relative start date of the price id in my grouped row? I am using postgresql.
A example to view what I want with my query:
DataSet:
ID | PRODUCT_ID | START_DATE | FINISH_DATE
1 1689 2018-01-19 02:00:00 2019-11-19 23:59:59
2 1689 2019-10-11 03:00:00 2019-10-15 23:59:59
3 1689 2019-01-11 03:00:00 2019-05-15 23:59:59
4 1690 2019-11-11 03:00:00 2019-12-15 23:59:59
5 1690 2019-05-11 03:00:00 2025-12-15 23:59:59
6 1691 2019-05-11 03:00:00 null
I want this result:
ID | PRODUCT_ID | START_DATE | FINISH_DATE
1 1689 2018-01-19 02:00:00 2019-11-19 23:59:59
5 1690 2019-05-11 03:00:00 2025-12-15 23:59:59
6 1691 2019-05-11 03:00:00 9999-12-31 23:59:59
The start date should be the same value of the row before the group by.
I would recommend DISTINCT ON in Postgres:
select distinct on (p.product_id) p.*
from prices p
order by p.product_id,
p.finish_date desc nulls first;
NULL values are treated as larger than any other value, so a descending sort puts them first. However, I've included nulls first just to be explicit.
DISTINCT ON is a very handy Postgres extension, which you can learn more about in the documentation.
Try this
with data as (
SELECT id, product_id,
max(COALESCE(finish_date,'9999-12-31')) as finish_date from prices group by 1,2)
select d.*, p.start_date from data d join prices p on p.id = d.id;
It surely isnt' the most elegant solution, but it should work.

SQL : How to check if my schedule is ok

I have a DB which looks like this :
FromDate ToDate ProfileUID
2017-02-10 07:00:00 2017-02-10 15:30:00 TB_D
2017-02-09 23:00:00 2017-02-10 07:00:00 ZK_D
2017-02-09 17:30:00 2017-02-09 23:00:00 DL_D
2017-02-09 07:00:00 2017-02-09 17:30:00 AM_D
2017-02-08 23:00:00 2017-02-09 07:00:00 CK_D
2017-02-08 17:30:00 2017-02-08 23:00:00 DJ_N
This DB is often modified manually and some errors can be done by user.
I'm trying to do an SQL query to check if the schedule is ok, which means I would like to know until when my records are (here we filled only for the two next days) and if there are no "black holes" in the schedule, meaning there is a period with no profileUID filled.
What I have so far :
SELECT *,LAG (ToDate) OVER (PARTITION BY FromDate ORDER BY FromDate)
FROM `dashboardcalendar`
WHERE FromDate>= NOW()
ORDER BY `dashboardcalendar`.`FromDate` ASC
EDIT: Given that you can't use window functions on your version of MariaDB, try this:
SELECT FromDate, ToDate, ProfileUID, next_FromDate, last_ToDate,
CASE WHEN next_record_uid is null then 'Warning - Gap after' end warn1,
CASE WHEN prev_record_uid is null then 'Warning - Gap before' end warn2
FROM (
SELECT t1.FromDate, t1.ToDate, t1.ProfileUID,
t2.ProfileUID next_record_uid,
t3.ProfileUID prev_record_uid
FROM table t1
LEFT JOIN table t2
ON t1.ToDate = t2.FromDate
LEFT JOIN table t3
ON t1.FromDate = t3.ToDate
)
This does not account for overlaps in time - it will give a false positive warning if two time periods overlap.
Original answer:
I'd use LEAD as well as LAG, and then wrap in another select to do the comparison:
SELECT FromDate, ToDate, ProfileUID, next_FromDate, last_ToDate,
CASE WHEN ToDate < next_FromDate then 'Warning - Gap after' end warn1,
CASE WHEN FromDate > last_ToDate then 'Warning - Gap before' end warn2
FROM (
SELECT FromDate, ToDate, ProfileUID,
LEAD(FromDate,1) OVER (ORDER BY FromDate) next_FromDate,
LAG(FromDate,1) OVER (ORDER BY FromDate) last_ToDate
FROM table
)
You should not need to partition, it sounds like from your description that each record covers a distinct period of time so there's nothing to group on.

SQL "transform" query

I have these data on a table (using SQL Server 2005):
ID ParentID StartTime EndTime
77 62 08:00:00 11:00:00
78 62 12:00:00 15:00:00
79 62 18:00:00 22:00:00
and I want to transform it into this:
ParentID BreakfastStart BreakfastEnd LunchStart LunchEnd DinnerStart DinnerEnd
62 08:00:00 11:00:00 12:00:00 15:00:00 18:00:00 22:00:00
Now the hard part is: assume I have no other data field specifying which record is breakfast, lunch or dinner. I want to associate them with lowest start time, i.e., the lower start time will be breakfast, next lower will be lunch and the higher will be dinner (assume all three (and only three) records are always filled).
Any ideas?
WITH q AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY parentID ORDER BY StartTime) AS rn
FROM mytable
)
SELECT qb.ParentID,
qb.StartTime AS BreakfastStart, qb.EndTime AS BreakfastEnd,
ql.StartTime AS LunchStart, ql.EndTime AS LunchEnd,
qd.StartTime AS DinnerStart, qd.EndTime AS DinnerEnd
FROM q qb
LEFT JOIN
q ql
ON ql.parentID = qb.parentID
AND ql.rn = 2
LEFT JOIN
q qd
ON qd.parentID = qb.parentID
AND qd.rn = 3
WHERE qb.rn = 1