MariaDB: Match requests with work performed where total > 0 using a case - datetime

I have a request table and a work table. For request type 1, 2, 4, or 5 I need to sum the work performed of type 6 or 7 where 6 represents effectively +1 and 7 represents -1. Exclude any requests where the request's work sum is <=0 or work were done before the most recent request.
The query details are:
Find requests type in (1, 2, 4, 5) by createDate.
For each request date found, sum work type in (6, 7) as +1 or -1 until the next request createDate.
Output any requests work sum > 0 before the next request.
The sample tables:
create table request
(
Id bigint not null,
userId bigint,
type bigint not null,
creationDate timestamp not null
);
create table work
(
Id bigint not null,
type bigint not null,
creationDate timestamp not null
);
The sample data:
insert into request (Id, userId, type, creationDate)
values (4, 45, 2, '2022-12-12 11:02:17'),
(9, 64, 2, '2022-12-12 01:01:18'),
(2, 92, 2, '2022-12-11 21:36:36'),
(2, 21, 2, '2022-12-11 21:25:54'),
(1, 3, 2, '2022-12-11 21:13:58'),
(7, 243, 2, '2022-12-11 21:04:05'),
(8, 24, 2, '2022-12-11 21:01:23');
insert into work (Id, type, creationDate)
values (3, 7, '2022-12-11 00:00:00'),
(6, 7, '2022-12-11 00:00:00'),
(11, 7, '2022-12-11 00:00:00'),
(6, 7, '2022-12-11 00:00:00'),
(1, 6, '2022-12-11 00:00:00'),
(2, 6, '2022-12-11 00:00:00'),
(11, 7, '2022-12-11 00:00:00'),
(5, 7, '2022-12-11 00:00:00'),
(1, 6, '2022-12-11 00:00:00'),
(11, 7, '2022-12-12 00:00:00'),
(4, 6, '2022-12-12 00:00:00'),
(8, 7, '2022-12-12 00:00:00');
The attempted query:
select id, sum(total), type, creationDate from (
select id, 0 as total, type, creationDate from request
union
select id, case type when 6 then 1 when 7 then -1 end as total, type, creationDate from work
) a where total > 0 group by id
This takes too long on live data, but works on small sets like this fiddle.
There is a challenge in the data, the timestamp for requests includes the time, but the work only has date with no timestamp.
The fiddle reports:
id
sum(total)
type
creationDate
1
1
6
2022-12-11 00:00:00
2
1
6
2022-12-11 00:00:00
4
1
6
2022-12-13 00:00:00
However both 1 and 2 are invalidated because the timestamp of the request is technically greater than the work. The expected output should be:
id
sum(total)
type
creationDate
4
1
6
2022-12-13 00:00:00
For id = 4, the work had the date of 2022-12-13 00:00:00 and the request was timestamped 2022-12-12 11:02:17.

One way to accomplish this is to use a subquery to join the two tables together and then group the results
Here's an example:
SELECT r.Id, SUM(CASE w.type WHEN 6 THEN 1 WHEN 7 THEN -1 END) as total, r.type, r.creationDate
FROM request r
JOIN (
SELECT Id, type, creationDate
FROM work
WHERE type IN (6,7)
) w ON w.creationDate >= r.creationDate
WHERE r.type IN (1,2,4,5)
GROUP BY r.Id, r.type, r.creationDate
HAVING total > 0

Related

sqlite, how to get the last n records whitout ORDER BY

I need to take the last 7 values of a column I was thinking of using 'LIMIT' but I don't know if it also works in reverse. Take an example, I have these values 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; I need to get 4, 5, 6, 7, 8, 9, 10, considering the fact that in the future other records will be added after 10. Is there a solution with 'LIMIT' or are there other solutions? I can't use ORDER BY 'couse the values i need get are the last 7 days of values and sqlite don't have date type and I don't registered date as milliseconds but i do in date format dd-mm-yyyy so use 'ORDER BY date' don't works. Thanks
If you change the format of the dates to YYYY-MM-DD then it is as simple as this:
SELECT columnname
FROM tablename
ORDER BY date DESC
LIMIT 7
As it is now the date format the only thing that you can do is construct the proper format in the ORDER BY clause with SUBSTR():
SELECT columnname
FROM tablename
ORDER BY SUBSTR(date, 7) || SUBSTR(date, 4, 2) || SUBSTR(date, 1, 2) DESC
LIMIT 7
This expression:
SUBSTR(date, 7) || SUBSTR(date, 4, 2) || SUBSTR(date, 1, 2)
transforms a date like 05-01-2020 to 20200105 so it is comparable and can be used to sort the rows.

How to handle non-max value ties in SQLite

I was given a database with the request to print out the top ten values of the database unless there was a tie - if there was a tie then print out all the values that tied with the 10th value. I read up a lot on handling ties with MAX and WITH TIES, but I can't seem to find a solution for printing out ties for something that is not a maximum value and using WITH TIES does not seem to help me. Is there some simple way to do this or do I have to step through some hoops?
For example, my database contains
10, 10, 10, 10, 9, 9, 9, 9, 8, 7, 7, 7, 7, 7, 2
The result I'm currently getting is with LIMIT 10, where my result is:
10, 10, 10, 10, 9, 9, 9, 9, 8, 7
But I would like for my program to return
10, 10, 10, 10, 9, 9, 9, 9, 8, 7, 7, 7, 7, 7
Would there be any way to do this?
You want the rank() window function (Requires sqlite 3.25 or newer):
sqlite> CREATE TABLE x(v INTEGER);
sqlite> INSERT INTO x VALUES (10),(10),(10),(10),(9),(9),(9),(9),(8),(7),(7),(7),(7),(7),(2);
sqlite> SELECT v, rnk
...> FROM (SELECT v, rank() OVER (ORDER BY v DESC) AS rnk FROM x)
...> WHERE rnk <= 10
...> ORDER BY rnk;
v rnk
---------- ----------
10 1
10 1
10 1
10 1
9 5
9 5
9 5
9 5
8 9
7 10
7 10
7 10
7 10
7 10
After sorting the numbers, each group of identical numbers is given the same ranking, with the next group's rank based on the total number of rows so far - why the 9's are rank 5 instead of 2 (For that behavior, use dense_rank() instead).
Would there be any way to do this?
I believe that the following will do what you want :-
SELECT * FROM mytable WHERE val >= (SELECT min(val) FROM (SELECT * FROM mytable ORDER BY val DESC LIMIT 10));
obviously the tablename mytable and the column name val may have to be changed to suit your table and column names.
That is the innermost query returns the 10 highest rows from which the lowest value is obtained, this driving the WHERE clause of the primary query.
Example/Demo
DROP TABLE IF EXISTS mytable;
CREATE TABLE IF NOT EXISTS mytable (val INTEGER);
INSERT INTO mytable VALUES (10), (10), (10), (10), (9), (9), (9), (9), (8), (7), (7), (7), (7), (7), (2);
SELECT * FROM mytable WHERE val >= (SELECT min(val) FROM (SELECT * FROM mytable ORDER BY val DESC LIMIT 10));
results in :-
If you actually wanted the result to be 10, 10, 10, 10, 9, 9, 9, 9, 8, 7, 7, 7, 7, 7 as a single row then you could use :-
SELECT group_concat(val) FROM mytable WHERE val >= (SELECT min(val) FROM (SELECT * FROM mytable ORDER BY val DESC LIMIT 10));

Check for particular sequence in sqlite3

I want to look for a particular sequence occurrences in database.
Example: Consider have following table:
Consider I want to check if table has following sequence or not : 555 222 444.
I want to display where sequence is not matching as:
Hoe can I do this?
Window functions (Added in Sqlite 3.25 so the following needs that or a newer version) make peeking at the next row easy:
Create and populate a sample table (Posting an image of your data is really not useful; text with either INSERT statements or CSV is best):
CREATE TABLE seq(num INTEGER PRIMARY KEY, value INTEGER);
INSERT INTO seq VALUES (1, 111), (2, 555), (3, 222), (4, 444), (5, 333)
, (6, 111), (7, 555), (8, 222), (9, 111), (10, 333)
, (11, 555), (12, 444), (13, 111), (14, 333), (15, 444);
And the query:
WITH leads AS (SELECT num, value
, lead(value, 1) OVER (ORDER BY num) AS next1
, lead(value, 2) OVER (ORDER BY num) AS next2
FROM seq)
SELECT CASE WHEN next1 = 222 AND next2 = 444 THEN 'MATCH' ELSE 'FAIL' END AS result
, num AS start
, CASE WHEN next1 = 222 AND next2 = 444 THEN num + 2 ELSE num + 1 END AS "end"
FROM leads
WHERE value = 555
ORDER BY num;
produces
result start end
---------- ---------- ----------
MATCH 2 4
FAIL 7 8
FAIL 11 12
(All of the above assumes you're using the num column to determine ordering)

How to deal with multi-modality?

I have the following data set in the form of {id, sample#, zone}:
[(1, 4, 5), (2, 3, 5), (3, 2, 2), (4, 1, 2)]
The following code:
cur.execute("SELECT zone FROM zone_temp GROUP BY zone HAVING COUNT(*) = (SELECT MAX(cnt) FROM(SELECT COUNT(*) as cnt FROM zone_temp GROUP BY zone) tmp)")
or:
cur.execute("SELECT zone FROM zone_temp GROUP BY zone ORDER BY COUNT(zone)")
returns only the first value that occurs the most (5). In this data set 5 and 2 are equally frequent and as such there really isn't a "most frequent" value. What is the best way to deal with this?

How do you declare an array of numbers in oracle pl/sql 11g

I have a set of numbers that i need to use in 2 queries. these are part of the same oracle SQL script for a 11g server:
update table1 set some_column = 1 where user_id in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
update table2 set some_other_column = 17 where user_id in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
How do you factor out that list of numbers into a variable and use it in both update statements?
You can use collections:
declare
type t_num is table of number;
num t_num;
begin
-- fill collection from query
select rownum
bulk collect into num
from dual connect by level < 10;
-- add one value to collection
num.extend;
num(num.last) := 345;
-- using in an UPDATE statement (the same you can use in INSERT and DELETE)
forall i in num.first..num.last
update table1 set some_column = 1 where user_id = num(i);
end;

Resources