How to select first item by group with condition? - sql

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.

Related

How can I create time range grouping in window function 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 )

how to use LIMIT in query of MS ACCESS

I want to do a paging system using php
i'am using access for my data base and it doesn't allow me to do a limit in my selected request.
SELECT * FROM table LIMIT 10,20
I want to add a column in my request so that i can have temporary IDs with successive integer that will allow me to do between condition
|id_temp | id | name |
| 1 | 56 | exmp |
| 2 | 180 | exmp |
| 3 | 193 | exmp |
| 4 | 194 | exmp |
| 5 | 363 | exmp |
| 6 | 500 | exmp |
I tried to use TOP as an alternative solution but it only allows me to select the top 10 and not the others.
Then I took the ID of my elements in order to use between but the problem is that i can not have the same amount everytime in my result. Moreover i have a really limited server and a big request so I can't use not in.
Access does not support OFFSET, so what you can do is a trick like this:
select top 10 *
from tablename
where id > (select max(id) from (select top 30 id from tablename order by id ))
order by id
it will return the rows from 31st to 40th ordered by id.
Note: never use TOP or LIMIT in sql without ORDER BY because the result is not guaranteed to be what you expect.
If you want that id_temp column, you can get it like this:
SELECT
((select count(*) from tablename where id < t.id)+1) AS id_temp,
t.*
FROM tablename AS t
ORDER BY t.id;

SQL - Selecting all latest unique records

I'm struggling a bit at creating an SQL query to select some records from an Access Database (using Excel VBA).
A cut of one of the tables (let's call it 'table1') has the following columns:
| my_id | your_id | phase |
| 1 | 1 | Open |
| 2 | 1 | Close |
| 3 | 2 | Open |
| 4 | 3 | Close |
| 5 | 2 | Close |
| 6 | 3 | Open |
The field 'my_id' will always be a unique value whereas the 'your_id' field may contain duplicates.
What I would like to do is select everything from the table for the most recent record of the 'your_id' where the phase is 'Close'. So that means in the above example table it would select 5, 4 & 2.
Hope this makes sense, sorry if not - I'm struggling to articulate what I mean!
Thanks
Although from ur example if u just add where conditin as phase='Close' u will get the records of 5,4 and 2. But I am assuming that there might be scenarios (not in ur example) where more than 1 record can come with status as Close for any given your_id so query should look like this
Select * from table1 where my_id in (
Select Max(My_Id) from table1 where phase='Close' group by your_id)

How to join rows in the same table by a shared column id with a where clause?

I'm trying to write a SQL query that looks at a single MySQL DB table (exp_playa_relationships) which stores relationship data of CMS' posts and which data structure looks like this:
rel_id | parent_entry_id | parent_field_id | child_entry_id
-----------------------------------------------------------------
55 | 3 | 2 | 1
56 | 3 | 2 | 4
58 | 1 | 2 | 4
59 | 8 | 4 | 2
60 | 8 | 5 | 1
63 | 4 | 2 | 3
64 | 9 | 4 | 6
65 | 9 | 5 | 3
rel_id is unique, other columns are not.
I would like to generate the following out of the data above:
event_data_id | user_id | event_id
--------------------------------------
8 | 1 | 2
9 | 3 | 6
The parent_field_id value itself is discarded in the final output but is needed to figure out if the row's child_entry_id signifies a user_id or event_id.
parent_entry_id is the event_data_id.
So in plain english I would like to:
Filter rows that have a parent_field_id value of either 4 or 5
Out of those rows, I want to join all those that share the same parent_entry_id.
Return the parent_entry_id as event_data_id.
Return the child_entry_id as a user_id if parent_field_id of the same row is 5.
Return the child_entry_id as a event_id if parent_field_id of the same row is 4.
My current SQL query (not working) is this:
SELECT
t1.`parent_entry_id` AS event_data_id,
t1.`child_entry_id` AS user_id,
t1.`child_entry_id` AS event_id
FROM `exp_playa_relationships` AS t1
INNER JOIN `exp_playa_relationships` AS t2
ON t1.`parent_entry_id` = t2.`parent_entry_id`
WHERE t1.`parent_field_id` = 4 OR t1.`parent_field_id` = 5
What I cannot figure out specifically is how to avoid creating duplicates on the parent_entry_id (SQL creates 2 new rows per row) and how to return child_entry_id as either user_id or event_id based on the parent_field_id value.
Any ideas would be much appreciated.
You're sooo close:
SELECT t1.`parent_entry_id` AS event_data_id,
t1.`child_entry_id` AS user_id,
t2.`child_entry_id` AS event_id
FROM `exp_playa_relationships` AS t1
INNER JOIN `exp_playa_relationships` AS t2
ON t2.`parent_entry_id` = t1.`parent_entry_id`
AND t2.`parent_field_id` = 4
WHERE t1.`parent_field_id` = 5
Specifically, you're having to tell it which row-set to pull the relevant data from.
By the way, your current database design will cause you more of these types of headaches... I'd recommend pulling the information out into 'result' tables (unless that's what this is for?).

SQL: Update column with with an index that resets if column changes

Firstly, sorry for the wording of the question. I'm not too sure how to express it. Hopefully the example below is clear.
If I have a table
Id | Type | Order
0 | Test | null
1 | Test | null
2 | Blah | null
3 | Blah | null
I want to turn it into this
Id | Type | Order
0 | Test | 1
1 | Test | 2
2 | Blah | 1
3 | Blah | 2
So I'm grouping the table by 'type' and allocating a number to 'order' incrementally. It'll start at 1 per type.
How should I go about doing it?
Db I'm using is Sybase 15.
select
Id,
Type,
row_number() over(partition by Type order by Id) as [Order]
from YourTable
You should utilize the ROW_NUMBER function to get you what you're looking for.
ROW_NUMBER (Transact-SQL)
Returns the sequential number of a row within a partition of a result
set, starting at 1 for the first row in each partition.