How can I create time range grouping in window function SQL - sql

I'm trying to create a grouping using multiple window function on SQL, the objective is to discern between different groups if there are some other groups in the middle. see below table
Part | time | expected result |
a | 11-29-2022 00:05:00.000 | 1 |
a | 11-29-2022 00:05:00.010 | 1 |
b | 11-29-2022 00:06:00.000 | 2 |
c | 11-29-2022 00:15:00.000 | 3 |
c | 11-29-2022 00:15:00.000 | 3 |
b | 11-29-2022 00:40:00.010 | 4 |
b | 11-29-2022 00:40:00.020 | 4 |
b | 11-29-2022 00:40:00.020 | 4 |
b | 11-29-2022 00:40:00.030 | 4 |
I'm doing something like:
Select part, time, count(*) over(Partition by Part order by time )
Lets focus in part "b", first occurrence is at minute 6, after that appears different parts and part b appears again at minute 40 so I need something like a time range to create the grouping
Also notice that sometimes the time is different in milliseconds even if the parts are consecutive (part b), those must belong to the same group.
Was trying to use the Rank window function but with 'range between' wasn't able to get that result.
Thanks!

Just another option via dense_rank()
Select *
,NewValue = dense_rank() over (order by convert(varchar(25),[Time],120))
From YourTable
Results

Please try this sql query.
Select part, time, dense_rank() over(Partition by Part )
or
Select part, time, dense_rank() over(Partition by Part order by time rows between unbounded preceding and unbounded following )

Related

How to select first item by group with condition?

I have a table with the following layout, to store orders for users, and to remember which orders are being processed right now:
Sequence | User | Order | InProcess
---------+------+-------+----------
1 | 1 | 1 |
2 | 1 | 2 |
3 | 2 | 1 |
4 | 3 | 1 |
5 | 1 | 3 |
6 | 4 | 1 |
7 | 2 | 2 |
E.g., line 4 | 3 | 1 | means that the 4th order ever is for user 3, and it's his/her 1st order. Now I want to select the order which to process next. This has to be done according to the following criterias:
Older orders (with lower sequence numbers) are processed first.
Only one order is processed per user at once.
Once an order is selected as being processed it gets marked as InProcess.
Once an order is completed, it is deleted from this list.
So, after some time this may look like this:
Sequence | User | Order | InProcess
---------+------+-------+----------
1 | 1 | 1 | X
2 | 1 | 2 |
3 | 2 | 1 | X
4 | 3 | 1 | X
5 | 1 | 3 |
6 | 4 | 1 |
7 | 2 | 2 |
When now being asked for the next order to process, the answer would be the line with sequence number 6, since orders for users 1, 2 and 3 are already being processed, so no additional order for them may be processed. The question is: How do I get efficiently to this row?
Basically what I need is the SQL equivalent of
Of all orders, select the first order which is not in process, and whose user is not having an order already being processed.
The question is just how to tell this with SQL? BTW: I'm looking for a standard SQL solution, not DBMS-specific ways to go. However, if for whatever reason limiting the question to a specific DBMS, these are the ones I have to support (in this order):
PostgreSQL
MariaDB
MySQL
SQL Server
MongoDB
Any ideas?
I think captures your logic:
select t.*
from (select t.*, max(in_process) over (partition by user_id) as any_in_process
from t
) t
where any_in_process is null
order by sequence
fetch first 1 row only;
Fetching one row is database specific, but the rest is pretty generic.
You can get the next order to be processed by using the ROW_NUMBER() window function, as in:
select *
from (
select
*,
row_number() over(order by "order", "sequence") as as rn
from t
where "user" not in (
select "user" from t where inprocess = 'X'
)
) x
where rn = 1
Available in PostgreSQL, MariaDB 10.2, MySQL 8.0, SQL Server 2012.

Column "f.price" must appear in the GROUP BY clause or be used in an aggregate function, but I've already used window function

I have a table with two columns: date and price. They both aren't unique.
I need to get running total in unique date order (one date - values sum for this date, next date - next sum + previous one and so on).
I know how to do this with subquery, but I want to use window functions:
There is a simple query:
SELECT f.date, SUM(f.price) OVER () FROM f GROUP BY f.date
It returns the error:
column f.price must appear in the GROUP BY clause or be used in an aggregate function
But I've already used aggregate function (SUM).
Can somebody tell me why this happend?
try avoiding over()
select f.date,
SUM(f.price)
from f
group by f.date
You are mixing window functions and aggregation, which is generally not a good idea. You are getting the error because, indeed, column f.price is not used in an aggregate function (it is used a window function).
I believe that the following query should give you what you want. It uses a window function, and relies on DISTINCT instead of aggregation.
SELECT DISTINCT fdate, SUM(fprice) OVER(ORDER BY fdate) FROM f ORDER BY fdate;
Demo on DB Fiddle:
Consider the following sample data, that seems to match your spec:
| fdate | fprice |
| ------------------------ | ------ |
| 2018-01-01T00:00:00.000Z | 1 |
| 2018-01-01T00:00:00.000Z | 2 |
| 2018-01-02T00:00:00.000Z | 3 |
| 2018-01-03T00:00:00.000Z | 4 |
| 2018-01-03T00:00:00.000Z | 1 |
The query would return:
| fdate | sum |
| ------------------------ | --- |
| 2018-01-01T00:00:00.000Z | 3 |
| 2018-01-02T00:00:00.000Z | 6 |
| 2018-01-03T00:00:00.000Z | 11 |

Difference between first and last row in grouping

I have this table taken from very complex query, that is, it's not possible to join on itself.
Rows are ordered by time desc.
type, value, time
+---+----+
| 2 | 2 |
| 2 | 7 |
| 3 | 20 |
| 3 | 16 |
+---+----+
I need to calculate the difference between first and last value per one type grouping, in the given example this will give me
+---+----+
| 2 | -5 |
| 3 | 4 |
+---+----+
Is it feasible?
One method uses window functions. Something like this works:
select distinct type,
(first_value(value) over (partition by type order by time asc) -
first_value(value) over (partition by type order by time desc)
) as diff
from t;
Unfortunately, Postgres doesn't have a first_value() aggregation function.
You could also do this using array_agg():
select distinct type,
((array_agg(value order by time asc))[1] -
(array_agg(value order by time desc))[1]
) as diff
from t
group by type;

sql subquery statement with a join

i am not able to think on how to build an sql statement that fits my needs, if this is just not possibl eplease let me know.
i have a table like this(ill ommit the columns not making dificulties to me)
table: servicio
------------------------------------------------------------
id | swo | date | issueValue |
1 | 15-001 | 2015-01-29 01:52:59 | 2
1 | 15-002 | 2015-01-30 01:24:00 | 2
------------------------------------------------------------
table: comments
------------------------------------------------------------
id | swo | date | Area |
1 | 15-001 | 2015-01-29 01:52:59 | 2
1 | 15-002 | 2015-01-30 01:24:00 | 1
1 | 15-002 | 2015-01-30 01:50:00 | 3
------------------------------------------------------------
i want to select the rows in servicio but include the latest Area assigned to each swo. the result should be something like this.
------------------------------------------------------------
id | swo | date | Area |
1 | 15-001 | 2015-01-29 01:52:59 | 2
1 | 15-002 | 2015-01-30 01:24:00 | 3
------------------------------------------------------------
so how can i make the sql statement check for top (1) and return the area value in it?
In SQL Server you can do this with apply, a subquery, or row_number(). Here is the first method:
select s.*, c.area
from servicio s outer apply
(select top 1 c.*
from comments c
where c.swo = s.swo
order by c.date desc
) c;
With this method, you can pull out additional columns if you like.
This is solution with Rownumber and subquery. In subquery you are selecting all rows which match between tables. Creating Ascending order for Comment table date column, Partitioning for every SWO (Partitioning means it will reset counter on every different SWO). 1 = Last date for particular SWO. In final select you just put WHERE clause with Rownum = 1
SELECT swo ,date ,issueValue ,Area
FROM
(SELECT
SRV.swo
,SRV.date
,SRV.issueValue
,CMNT.Area
,ROW_NUMBER() OVER (PARTITION BY CMNT.swo ORDER BY CMNT.date DESC) AS Dsc_Rank
FROM servicio AS SRV
LEFT JOIN comments AS CMNT
ON (SRV.swo=CMNT.swo)
) AS Temp
WHERE Temp.Dsc_Rank = 1 /* Descending Date Rank 1 = Latest date from Comment table*/
Note: for example in Teradata you can omit sub query and use Qualify clause

Find percentage of first row within the same column

I have a table that looks like this:
Day | Count
----+------
1 | 59547
2 | 40448
3 | 36707
4 | 34492
And I want it to query a result set like this:
Day | Count | Percentage of 1st row
----+-------+----------------------
1 | 59547 | 1
2 | 40448 | .6793
3 | 36707 | .6164
4 | 34492 | .5792
I've tried using window functions, but can only seem to get a percentage of the total, which looks like this:
Day | Count | Percentage of 1st row
----+-------+----------------------
1 | 59547 | 0.347833452
2 | 40448 | 0.236269963
3 | 36707 | 0.214417561
4 | 34492 | 0.201479024
But I want a percentage of the first row. I know I can use a cross join, that queries just for "Day 1" but that seems to take a long time. I was wondering if there was a way to write a window function to do this.
Judging from your numbers, you may be looking for this:
SELECT *, round(ct::numeric/first_value(ct) OVER (ORDER BY day), 4) AS pct
FROM tbl;
"A percentage for each row, calculated as ct divided by ct of the first row as defined by the smallest day number."
The key is the window function first_value().
-> SQLfiddle demo.