Get the following record in query - sql

If we have a table called Activity and has rows[ActivityCode and StartTime]
for example
ActivityCode-----StartTime<BR>
Lunch------------1200<BR>
MathClass--------1300<BR>
EnglishClass-----1500<BR>
EndOfSchool------1700<BR>
And now I want to make one SQL Query to display as follow:
ActivityCode-----StartTime-----EndTime<BR>
Lunch------------1200----------1300<BR>
MathClass--------1300----------1500<BR>
EnglishClass-----1500----------1700<BR>
EndOfSchool------1700----------1700<BR>
I am not sure how to do it. I tried to follow How to get a value from previous result row of a SELECT statement?. But it didn't work as I expected. Any help is appreciated.

You can use this query:
SELECT
Activity.ActivityCode,
Activity.StartTime,
Nz((Select Top 1 StartTime
From Activity As T
Where T.StartTime > Activity.StartTime
Order By StartTime Asc),
[StartTime]) AS EndTime,
CDate(TimeSerial(Val([EndTime])\100,Val([EndTime]) Mod 100,0)-
TimeSerial(Val([StartTime])\100,Val([StartTime]) Mod 100,0)) AS Duration
FROM
Activity;
Output:

I would use a subquery with aggregation:
select a.*,
(select nz(min(a2.starttime), a.endtime)
from activity as a2
where a2.starttime > a.starttime
) as endtime
from activity as a;
Normally in such an example, there would be an additional column identifying a "grouping" of some sort -- such as a person. If you have such a column, you would have an equality condition in the subquery as well as the inequality on time.
Also, there are much better ways to do this in almost any other database -- notably, the lead() function.

Related

Subquery sorted by another table

This is a follow-up question to Get the following record in query.
But the task is a bit more complicated. I tried to modify the SQL query but I was not able to fulfill the task.
If we have two tables, one called Activity and has rows [ActivityCode and StartTime], and the another one called Students has rows [Name and ID]
for example:
Name-----ID-----ActivityCode-----StartTime<BR>
Tom------123------Lunch------------1200<BR>
Tom------123------MathClass--------1300<BR>
Tom------123------EnglishClass-----1500<BR>
Tom------123------EndOfSchool------1700<BR>
Mary-----369-----Lunch------------1200<BR>
Mary-----369-----ScienceClass-----1300<BR>
Mary-----369-----EnglishClass-----1600<BR>
Mary-----369-----EndOfSchool------1700<BR>
And now I want to make one SQL Query to display as follow:
Name-----ID------ActivityCode-----StartTime------EndTime<BR>
Tom------123--- Lunch------------1200-----------1300<BR>
Tom------123-----MathClass--------1300-----------1500<BR>
Tom------123-----EnglishClass-----1500-----------1700<BR>
Tom------123-----EndOfSchool------1700-----------1700<BR>
Mary-----369-----Lunch------------1200-----------1300<BR>
Mary-----369-----ScienceClass-----1300-----------1600<BR>
Mary-----369-----EnglishClass-----1600-----------1700<BR>
Mary-----369-----EndOfSchool------1700-----------1700<BR>
I follow the code, credits to Gustav:
SELECT
Activity.ActivityCode,
Activity.StartTime,
Nz((Select Top 1 StartTime
From Activity As T
Where T.StartTime > Activity.StartTime
Order By StartTime Asc),
[StartTime]) AS EndTime,
CDate(TimeSerial(Val([EndTime])\100,Val([EndTime]) Mod 100,0)-
TimeSerial(Val([StartTime])\100,Val([StartTime]) Mod 100,0)) AS Duration
FROM
Activity;
I tried to modify the part
Order By StartTime Asc
Because the whole query is sorted according to the Student ID which is from another table. But some message boxes popped up and I couldn't solve it. How can I modify it? thank you.
Consider this SQL:
SELECT Students.ID, [Name], ActivityCode, StartTime,
Nz((Select Top 1 StartTime FROM Activity As T
WHERE T.StartTime > Activity.StartTime AND T.ID=Activity.ID
ORDER BY StartTime Asc),Startime) AS EndTime,
DateDiff("h",TimeSerial(StartTime/100,0,0),TimeSerial(EndTime/100,0,0)) AS Duration
FROM Students INNER JOIN Activity ON Students.ID=Activity.ID;

Why are different result between use date_part and exactly date parameter query data in peroid date?

I'm try to count distinct value in some columns in a table.
i have a logic and i try to write in 2 way
But i get diffent results from this two query.
Can any one help to clarify me? I dont know what wrong is code or i think.
SQL
select count(distinct membership_id) from members_membership m
where date_part(year,m.membership_expires)>=2019
and date_part(month,m.membership_expires)>=7
and date_part(day,m.membership_expires)>=1
and date_part(year,m.membership_creationdate)<=2019
and date_part(month,m.membership_creationdate)<=7
and date_part(day,m.membership_creationdate)<=1
;
select count(distinct membership_id) from members_membership m
where m.membership_expires>='2019-07-01'
and m.membership_creationdate<='2019-07-01'
;
I actually think that this is the query you intend to run:
SELECT
COUNT(DISTINCT membership_id)
FROM members_membership m
WHERE
m.membership_expires >= '2019-07-01' AND
m.membership_creationdate < '2019-07-01';
It doesn't make sense for a membership to expire at the same moment it gets created, so if it expires on midnight of 1st-July 2019, then it should have been created strictly before that point in time.
That being said, the problem with the first query is that, e.g., the restriction on the month being on or before July would apply to every year, not just 2019. It is difficult to write a date inequality using the year, month, and day terms separately. For this reason, the second version you used is preferable. It is also sargable, meaning that an index on membership_expires or membership_creationdate can be used.
There is an issue with the first query:
select count(distinct membership_id) from members_membership m
where date_part(year,m.membership_expires)>=2019
and date_part(month,m.membership_expires)>=7
and date_part(day,m.membership_expires)>=1
and date_part(year,m.membership_creationdate)<=2019
and date_part(month,m.membership_creationdate)<=7
and date_part(day,m.membership_creationdate)<=1; -- do you think that any day is less than 1??
-- this condition will be satisfy by only 01-Jul-2019, But I think you need all the dates before 01-Jul-2019
and date_part(day,m.membership_creationdate)<=1 is culprit of the issue.
even membership_creationdate = 15-jan-1901 will not satisfy above condition.
You need to always use date functions on date columns to avoid such type of issue. (Your second query is perfectly fine)
Cheers!!
The reason could be due to a time component.
The proper comparison for the first query is:
select count(distinct membership_id)
from members_membership m
where m.membership_expires >= '2019-07-01' and
m.membership_creationdate < '2019-07-02'
--------------------------------^ not <= ---^ next day
This logic should work regardless of whether or not the "date" has a time component.

SQL group by 1 column but include TOP 1 of other columns

I am trying to build a SQL query where I group by 1 column, but then also include the values of other columns from an arbitrary record in each group. So, something like
SELECT BoxNo
FROM MuffinData
WHERE FrostingTimeApplied >= CONVERT(date, GETDATE())
GROUP BY BoxNo
but including some value from columns MuffinType, FrostingType in the result (I know that there will be only 1 value of MuffinType and FrostingType per box.)
You have to use an aggregate function for each column selected that is not present in the GROUP BY clause:
SELECT BoxNo, MAX(MuffinType) AS MuffinType, MAX(FrostingType) AS FrostingType
FROM MuffinData
WHERE FrostingTimeApplied >= CONVERT(date, GETDATE())
GROUP BY BoxNo
If there is only 1 value of MuffinType and FrostingType per box, then these unique values per box no are going to be selected in the above query.
I know that there will be only 1 value of MuffinType and FrostingType
per box
If that's indeed the case, a simple DISTINCT should do the trick, like so:
SELECT DISTINCT BoxNo, MuffinType, FrostingType
FROM MuffinData
WHERE FrostingTimeApplied >= CONVERT(date, GETDATE());
If that's not the case, you're dealing with a problem known generally as the Top N per group problem. You can find coverage of the problem and suggested solutions here.
Cheers,
Itzik
If you're grouping by anything, then the only way to do this in a single statement (that I'm aware of) is to have the other columns you're returning be the result of an aggregate function. Aggregate functions are anything that take multiple values but return you a single result like: SUM, MAX, MIN, COUNT, etc...
SELECT BoxNo, COUNT(MuffinData.ID), MAX(FrostingType.FlavorID) FROM MuffinData, FrostingType etc...
You might have to adjust your WHERE logic or have another data source in your FROM list (subquery).
You can use a CTE and join back to the original table to get the fields you want. In this case,
WITH BoxGroup AS (SELECT BoxNo FROM MuffinData WHERE FrostingTimeApplied >= CONVERT(date, GETDATE()) GROUP BY BoxNo) SELECT md.BoxNo,md.MuffinType,md.FrostingType FROM MuffinData md INNER JOIN BoxGroup bg ON bg.BoxNo = md.BoxNo

SQL Average Inter-arrival Time, Time Between Dates

I have a table with sequential timestamps:
2011-03-17 10:31:19
2011-03-17 10:45:49
2011-03-17 10:47:49
...
I need to find the average time difference between each of these(there could be dozens) in seconds or whatever is easiest, I can work with it from there. So for example the above inter-arrival time for only the first two times would be 870 (14m 30s). For all three times it would be: (870 + 120)/2 = 445 (7m 25s).
A note, I am using postgreSQL 8.1.22 .
EDIT: The table I mention above is from a different query that is literally just a one-column list of timestamps
Not sure I understood your question completely, but this might be what you are looking for:
SELECT avg(difference)
FROM (
SELECT timestamp_col - lag(timestamp_col) over (order by timestamp_col) as difference
FROM your_table
) t
The inner query calculates the distance between each row and the preceding row. The result is an interval for each row in the table.
The outer query simply does an average over all differences.
i think u want to find avg(timestamptz).
my solution is avg(current - min value). but since result is interval, so add it to min value again.
SELECT avg(target_col - (select min(target_col) from your_table))
+ (select min(target_col) from your_table)
FROM your_table
If you cannot upgrade to a version of PG that supports window functions, you
may compute your table's sequential steps "the slow way."
Assuming your table is "tbl" and your timestamp column is "ts":
SELECT AVG(t1 - t0)
FROM (
-- All this silliness would be moot if we could use
-- `` lead(ts) over (order by ts) ''
SELECT tbl.ts AS t0,
next.ts AS t1
FROM tbl
CROSS JOIN
tbl next
WHERE next.ts = (
SELECT MIN(ts)
FROM tbl subquery
WHERE subquery.ts > tbl.ts
)
) derived;
But don't do that. Its performance will be terrible. Please do what
a_horse_with_no_name suggests, and use window functions.

Possible to use SQL to sort by date but put null dates at the back of the results set?

I have a bunch of tasks in a MySQL database, and one of the fields is "deadline date". Not every task has to have to a deadline date.
I'd like to use SQL to sort the tasks by deadline date, but put the ones without a deadline date in the back of the result set. As it is now, the null dates show up first, then the rest are sorted by deadline date earliest to latest.
Any ideas on how to do this with SQL alone? (I can do it with PHP if needed, but an SQL-only solution would be great.)
Thanks!
Here's a solution using only standard SQL, not ISNULL(). That function is not standard SQL, and may not work on other brands of RDBMS.
SELECT * FROM myTable
WHERE ...
ORDER BY CASE WHEN myDate IS NULL THEN 1 ELSE 0 END, myDate;
SELECT * FROM myTable
WHERE ...
ORDER BY ISNULL(myDate), myDate
SELECT foo, bar, due_date FROM tablename
ORDER BY CASE ISNULL(due_date, 0)
WHEN 0 THEN 1 ELSE 0 END, due_date
So you have 2 order by clauses. The first puts all non-nulls in front, then sorts by due date after that
The easiest way is using the minus operator with DESC.
SELECT * FROM request ORDER BY -date DESC
In MySQL, NULL values are considered lower in order than any non-NULL value, so sorting in ascending (ASC) order NULLs are listed first, and if descending (DESC) they are listed last.
When a - (minus) sign is added before the column name, NULL become -NULL.
Since -NULL == NULL, adding DESC make all the rows sort by date in ascending order followed by NULLs at last.