In MYSQL there are 2 columns in table column A and column B and if in column A continuously comes one 10th time and in column B 11th time comes true(B can be 1 or 0 between these 10 times ) so I want that column id of B.
+----+---+---+
| id | A | B |
+----+---+---+
| 1 | 1 | 0 |
| 2 | 0 | 1 |
| 3 | 1 | 0 |
| 4 | 1 | 0 |
| 5 | 1 | 0 |
| 6 | 1 | 1 |
| 7 | 1 | 0 |
| 8 | 1 | 1 |
| 9 | 1 | 1 |
| 10 | 1 | 0 |
| 11 | 1 | 1 |
| 12 | 1 | 0 |
| 13 | 0 | 1 |
+----+---+---+
I need this (column B id) (Where Column A continuously come 1 (10 times) and Column B (11th id after contenious 10 time 1 in column A )
You could use a running total in a sub query to help you with this on versions prior to mysql 8.0
drop table if exists t;
create table t
(id int,A int,B int);
insert into t values
(1, 1 ,0),
(2, 0 ,1),
(3, 1 ,0),
(4, 1 ,0),
(5, 1 ,0),
(6, 1 ,1),
(7, 1 ,0),
(8, 1 ,1),
(9, 1 ,1),
(10, 1 ,0),
(11, 1 ,1),
(12, 1 ,0),
(13, 1 ,1),
(14, 1 ,1),
(15, 1 ,1),
(16, 0 ,1);
select t1.id,t1.a,t1.b
from
(
select t.*,
if(t.a = 1, #rt:=#rt+1,#rt:=0) rt
from t
cross join (select #rt:=0) r
order by t.id
) t1
where t1.rt >= 10;
+------+------+------+
| id | a | b |
+------+------+------+
| 12 | 1 | 0 |
| 13 | 1 | 1 |
| 14 | 1 | 1 |
| 15 | 1 | 1 |
+------+------+------+
4 rows in set (0.00 sec)
This is a complicated one. Using window functions available in MySQL 8.0, I would proceed in 3 steps:
first compute row numbers in the overall group and in groups of A values
then do a cumulative sum of A values within groups of consecutive A values (using the difference between the 2 above groups), while using LEAD() to recover the id and B value of the next record
finally, filter on the record whose cumulative SUM is 10 and whose next B value is 1; the id of the next record is what you are looking for
Query:
SELECT leadID id
FROM (
SELECT
id,
SUM(A) OVER(PARTITION BY rn1 - rn2 ORDER BY id) sm,
LEAD(id) OVER(ORDER BY id) leadID,
LEAD(B) OVER(ORDER BY id) leadB
FROM (
SELECT
id,
A,
B,
ROW_NUMBER() OVER(ORDER BY id) rn1,
ROW_NUMBER() OVER(PARTITION BY A ORDER BY id) rn2
FROM mytable
) x
) x
WHERE sm = 10 AND leadB = 1
This demo on DB Fiddle with your sample data yields:
| id |
| --- |
| 13 |
Related
I would like to display a players current score as well as how many points they have gained within a selected time frame.
I have 2 tables
skills table
+----+---------+---------------------+
| id | name | created_at |
+----+---------+---------------------+
| 1 | skill 1 | 2020-06-05 00:00:00 |
| 2 | skill 2 | 2020-06-05 00:00:00 |
| 3 | skill 3 | 2020-06-05 00:00:00 |
+----+---------+---------------------+
scores table
+----+-----------+----------+-------+---------------------+
| id | player_id | skill_id | score | created_at |
+----+-----------+----------+-------+---------------------+
| 1 | 1 | 1 | 5 | 2020-06-06 00:00:00 |
| 2 | 1 | 1 | 10 | 2020-07-06 00:00:00 |
| 3 | 1 | 2 | 1 | 2020-07-06 00:00:00 |
| 4 | 2 | 1 | 11 | 2020-07-06 00:00:00 |
| 5 | 1 | 1 | 13 | 2020-07-07 00:00:00 |
| 6 | 1 | 2 | 10 | 2020-07-07 00:00:00 |
| 7 | 2 | 1 | 12 | 2020-07-07 00:00:00 |
| 8 | 1 | 1 | 20 | 2020-07-08 00:00:00 |
| 9 | 1 | 2 | 15 | 2020-07-08 00:00:00 |
| 10 | 2 | 1 | 17 | 2020-07-08 00:00:00 |
+----+-----------+----------+-------+---------------------+
my expected results are:-
24 hour query
+-----------+---------+-------+------+
| player_id | name | score | gain |
+-----------+---------+-------+------+
| 1 | skill 1 | 20 | 7 |
| 1 | skill 2 | 15 | 5 |
+-----------+---------+-------+------+
7 day query
+-----------+---------+-------+------+
| player_id | name | score | gain |
+-----------+---------+-------+------+
| 1 | skill 1 | 20 | 10 |
| 1 | skill 2 | 15 | 14 |
+-----------+---------+-------+------+
31 day query
+-----------+---------+-------+------+
| player_id | name | score | gain |
+-----------+---------+-------+------+
| 1 | skill 1 | 20 | 15 |
| 1 | skill 2 | 15 | 14 |
+-----------+---------+-------+------+
so far I have the following, but all this does is return the last 2 records for each skill, I am struggling to calculate the gains and the different time frames
SELECT player_id, skill_id, name, score
FROM (SELECT player_id, skill_id, name, score,
#skill_count := IF(#current_skill = skill_id, #skill_count + 1, 1) AS skill_count,
#current_skill := skill_id
FROM skill_scores
INNER JOIN skills
ON skill_id = skills.id
WHERE player_id = 1
ORDER BY skill_id, score DESC
) counted
WHERE skill_count <= 2
I would like some help figuring out the query I need to build to get the desired results, or is it best to do this with php instead of in the db?
EDIT:-
MYSQL 8.0.20 dummy data id's are primary_key auto increment but I didnt ad that for simplicity:-
CREATE TABLE skills
(
id bigint,
name VARCHAR(80)
);
CREATE TABLE skill_scores
(
id bigint,
player_id bigint,
skill_id bigint,
score bigint,
created_at timestamp
);
INSERT INTO skills VALUES (1, 'skill 1');
INSERT INTO skills VALUES (2, 'skill 2');
INSERT INTO skills VALUES (3, 'skill 3');
INSERT INTO skill_scores VALUES (1, 1, 1 , 5, '2020-06-06 00:00:00');
INSERT INTO skill_scores VALUES (2, 1, 1 , 10, '2020-07-06 00:00:00');
INSERT INTO skill_scores VALUES (3, 1, 2 , 1, '2020-07-06 00:00:00');
INSERT INTO skill_scores VALUES (4, 2, 1 , 11, '2020-07-06 00:00:00');
INSERT INTO skill_scores VALUES (5, 1, 1 , 13, '2020-07-07 00:00:00');
INSERT INTO skill_scores VALUES (6, 1, 2 , 10, '2020-07-07 00:00:00');
INSERT INTO skill_scores VALUES (7, 2, 1 , 12, '2020-07-07 00:00:00');
INSERT INTO skill_scores VALUES (8, 1, 1 , 20, '2020-07-08 00:00:00');
INSERT INTO skill_scores VALUES (9, 1, 2 , 15, '2020-07-08 00:00:00');
INSERT INTO skill_scores VALUES (10, 2, 1 , 17, '2020-07-08 00:00:00');
WITH cte AS (
SELECT id, player_id, skill_id,
FIRST_VALUE(score) OVER (PARTITION BY player_id, skill_id ORDER BY created_at DESC) score,
FIRST_VALUE(score) OVER (PARTITION BY player_id, skill_id ORDER BY created_at DESC) - FIRST_VALUE(score) OVER (PARTITION BY player_id, skill_id ORDER BY created_at ASC) gain,
ROW_NUMBER() OVER (PARTITION BY player_id, skill_id ORDER BY created_at DESC) rn
FROM skill_scores
WHERE created_at BETWEEN #current_date - INTERVAL #interval DAY AND #current_date
)
SELECT cte.player_id, skills.name, cte.score, cte.gain
FROM cte
JOIN skills ON skills.id = cte.skill_id
WHERE rn = 1
ORDER BY player_id, name;
fiddle
Ps. I don't understand where gain=15 is taken for 31-day period - the difference between '2020-07-08 00:00:00' and '2020-06-06 00:00:00' is 32 days.
Well i think you need a (temporary) table for this. I will call it "player_skill_gains". Its basically the players skills ordered by created_at and with an auto_incremented id:
CREATE TABLE player_skill_gains
(`id` int PRIMARY KEY AUTO_INCREMENT NOT NULL
, `player_id` int
, skill_id int
, score int
, created_at date)
;
INSERT INTO player_skill_gains(player_id, skill_id, score, created_at)
SELECT player_skills.player_id AS player_id
, player_skills.skill_id
, SUM(player_skills.score) AS score
, player_skills.created_at
FROM player_skills
GROUP BY player_skills.id, player_skills.skill_id, player_skills.created_at
ORDER BY player_skills.player_id, player_skills.skill_id, player_skills.created_at ASC;
Using this table we can relatively easily select the last skill for each row (id-1). Using this we can calculate the gains:
SELECT player_skill_gains.player_id, skills.name, player_skill_gains.score
, player_skill_gains.score - IFNULL(bef.score,0) AS gain
, player_skill_gains.created_at
FROM player_skill_gains
INNER JOIN skills ON player_skill_gains.skill_id = skills.id
LEFT JOIN player_skill_gains AS bef ON (player_skill_gains.id - 1) = bef.id
AND player_skill_gains.player_id = bef.player_id
AND player_skill_gains.skill_id = bef.skill_id
For the different queries you want to have (24 hours, 7 days, etc.) you just have to specify the needed where-part for the query.
You can see all this in action here: http://sqlfiddle.com/#!9/1571a8/11/0
Abstract question
I have a sql-table that contains records in the following form:
(list_id, value) where the list_id is an Integer identifiing a specific list and the value is something that has an order.
I now struggle to write a sql query that returns all records of that table at first ordered by the rank the list has compared to the other lists and then ordered by the value.
The abstract problem is, that I want to sort a list of lists using sql.
Algorithm to compare two lists
The algorithm to compare two lists is the following:
data CompareRes = FirstSmaller | FirstGreater | Equal deriving Show
compareLists :: Ord a => [a] -> [a] -> CompareRes
compareLists [] [] = Equal
-- Longer lists are considered to be smaller
compareLists _ [] = FirstSmaller
compareLists [] _ = FirstGreater
compareLists (x:xs) (y:ys)
| x < y = FirstSmaller
| x > y = FirstGreater
| otherwise = compareLists xs ys
Details
In my specific case the values are all Dates.
So my table looks like this:
CREATE TABLE `list_date` (
`list_id` INT NOT NULL,
`date` DATE NOT NULL,
PRIMARY KEY (`list_id`, `date`)
);
I'm using a mysql:8.0 database so a solution using WINDOW-functions is acceptable.
Example
Data
INSERT INTO `list_date` VALUES
(1, '2019-11-02'), (1, '2019-11-03'), (1, '2019-11-04'), (1, '2019-11-05'), (1, '2019-11-07'), (1, '2019-11-08'), (1, '2019-11-09'),
(2, '2019-11-01'), (2, '2019-11-03'), (2, '2019-11-04'),
(3, '2019-11-01'), (3, '2019-11-02'), (3, '2019-11-03'),
(4, '2019-11-02'), (4, '2019-11-04'), (4, '2019-11-13'), (4, '2019-11-14'),
(5, '2019-11-03'), (5, '2019-11-04'), (5, '2019-11-05'), (5, '2019-11-10'),
(6, '2019-11-01'), (6, '2019-11-02'), (6, '2019-11-03'), (6, '2019-11-05');
Query
Where I really struggle is to create an expression that calculates the list_rank:
SELECT
`list_id`,
`date`,
<PLEASE HELP> as `list_rank`
FROM
`list_date`
ORDER BY
`list_rank`, `date`;
Expected result
| list_id | date | list_rank |
|---------|------------|-----------|
| 6 | 2019-11-01 | 1 |
| 6 | 2019-11-02 | 1 |
| 6 | 2019-11-03 | 1 |
| 6 | 2019-11-05 | 1 |
| 3 | 2019-11-01 | 2 |
| 3 | 2019-11-02 | 2 |
| 3 | 2019-11-03 | 2 |
| 2 | 2019-11-01 | 3 |
| 2 | 2019-11-03 | 3 |
| 2 | 2019-11-04 | 3 |
| 1 | 2019-11-02 | 4 |
| 1 | 2019-11-03 | 4 |
| 1 | 2019-11-04 | 4 |
| 1 | 2019-11-05 | 4 |
| 1 | 2019-11-07 | 4 |
| 1 | 2019-11-08 | 4 |
| 1 | 2019-11-09 | 4 |
| 4 | 2019-11-02 | 5 |
| 4 | 2019-11-04 | 5 |
| 4 | 2019-11-13 | 5 |
| 4 | 2019-11-14 | 5 |
| 5 | 2019-11-03 | 6 |
| 5 | 2019-11-04 | 6 |
| 5 | 2019-11-05 | 6 |
| 5 | 2019-11-10 | 6 |
or
That image is the current live result my application produces. Currently the sorting is implemented using Java.
Edit
After not receiving a better answer, I implemented a solution as suggested by #gordon-linoff:
SELECT
`list_id`,
`date`
FROM
`list_date`
INNER JOIN (
SELECT `sub`.`list_id`,
GROUP_CONCAT(`sub`.`date` ORDER BY `sub`.`date` SEPARATOR '') as `concat_dates`
FROM `list_date` as `sub`
GROUP BY `sub`.`list_id`
) `all_dates` ON (`all_dates`.`list_id` = `list_date`.`list_id`)
ORDER BY
`all_dates`.`concat_dates`, `date`;
I've also created an SQL Fiddle - So you can play around with your solution.
But this solution does not sort the lists as expected because longer lists are considered bigger than smaller lists.
So I am still hoping to receive a solution that solves 100% of my requirements :)
If I understand correctly, you can sort the lists by the dates concatenated together:
select ld.*
from list_date ld join
(select list_id, group_concat(date) as dates
from ld
group by list_id
) ldc
on ld.list_id = ldc.list_id
order by ldc.dates, ld.date;
Since it's for MySql 8 the window functions can be used for this (yay).
Here's a query that first calculates some metrics, to use in the calculation of the ranking:
SELECT
list_id,
`date`,
DENSE_RANK() OVER (ORDER BY ListMinDate ASC, ListCount DESC, ListMaxDate, list_id) AS list_rank
FROM
(
SELECT
list_id,
`date`,
COUNT(*) OVER (PARTITION BY list_id) AS ListCount,
MIN(`date`) OVER (PARTITION BY list_id) AS ListMinDate,
MAX(`date`) OVER (PARTITION BY list_id) AS ListMaxDate
FROM list_date
) q
ORDER BY list_rank, `date`
A test on db<>fiddle here
I got this table:
id | score
1 | 1
2 | 4
3 | 4
4 | 3
5 | 2
6 | 2
7 | 1
8 | 4
9 | 2
10 | 3
I need to order it by score desc:
id | score
2 | 4
3 | 4
8 | 4
4 | 3
10 | 3
5 | 2
6 | 2
9 | 2
1 | 1
7 | 1
and get first 3 rows which starts with id 6
So the result should be:
6 | 2
9 | 2
1 | 1
Is this possible? Thanks in advance
I would approach this with a cumulative sum() (available in MySQL 8.0):
select
id,
score
from mytable
order by
sum(id = 6) over(order by score desc, id) desc,
score desc,
id
limit 3
The sum() orders record in the required direction; as soon as the record that has id = 6 is met, the sum takes value 1. It allows to put these records on top. The rest is just adding the additional sorting criteria and limiting the number of results.
Demo on DB Fiddle:
| id | score |
| --- | ----- |
| 6 | 2 |
| 9 | 2 |
| 1 | 1 |
In earlier versions of mysql, you can emulate the window sum with a user variable, as follows:
select
id,
score
from
(select #sm := 0) s
cross join (select id, score from mytable order by score desc, id) t
order by
case when id = 6 then #sm := #sm + 1 end desc,
score desc,
id
limit 3
Demo on DB Fiddle: same results
With this:
select t.*
from tablename t cross join (select * from tablename where id = 6) c
where t.score < c.score or (t.score = c.score and t.id >= c.id)
order by t.score desc, t.id
limit 3
See the demo.
Results:
| id | score |
| --- | ----- |
| 6 | 2 |
| 9 | 2 |
| 1 | 1 |
With this table
CREATE TABLE table3
(`id` int, `score` int)
;
INSERT INTO table3
(`id`, `score`)
VALUES
(1, 1),
(2, 4),
(3, 4),
(4, 3),
(5, 2),
(6, 2),
(7, 1),
(8, 4),
(9, 2),
(10, 3)
;
And this select
SELECT `id`, `score`
FROM (SELECT `id`,`score`,if (id = 8,#scoreid:= #scoreid +1,#scoreid) scoreid
From table3, (SELECT #scoreid :=0) s Order by score desc) t1
Where scoreid > 0 LIMIT 3;
you get
id score
8 4
4 3
10 3
DBFiddle example https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=95e2051d560c2ac27fdcc8f9d04acf5d
I have a table with below-mentioned columns:
ID (int) AUTO_INCREMENT PRIMARY KEY
DOCTOR_ID (int)
PATIENT_IN_TIME (datetime)
AVG_CHECKUP_TIME
I want to subtract row 1 PATIENT_IN_TIME with row 2 PATIENT_IN_TIME and save the result in minutes to AVG_CHECKUP_TIME.
Suppose there are 5 entries in the table.
|1|2|2018-03-22 02:49:51|NULL|
|2|2|2018-03-22 02:56:37|NULL|
So I want to find the difference of both the rows and save the minutes in the last column. So, the output would look like,
|1|2|2018-03-22 02:49:51|7|
|2|2|2018-03-22 02:56:37|NULL|
Please let me know if you need more information.
create table tbl
(
id int auto_increment primary key,
doctor_id int,
patient_in_time datetime,
avg_checkup_time datetime
);
insert into tbl values
(1, 2, '2018-03-22 02:49:51', null),
(2, 2, '2018-03-22 02:56:37', null),
(3, 2, '2018-03-22 03:00:15', null),
(4, 2, '2018-03-22 03:03:37', null);
select t1.id, t1.doctor_id, t1.patient_in_time,
timestampdiff(minute, t1.patient_in_time,
(select patient_in_time
from tbl where id = t1.id +1)) diff
from tbl t1
id | doctor_id | patient_in_time | diff
-: | --------: | :------------------ | ---:
1 | 2 | 2018-03-22 02:49:51 | 6
2 | 2 | 2018-03-22 02:56:37 | 3
3 | 2 | 2018-03-22 03:00:15 | 3
4 | 2 | 2018-03-22 03:03:37 | null
dbfiddle here
As per comments if order is set by patient_in_time then you can use an scalar subquery that returns next row in this way:
select t1.id,
t1.doctor_id,
t1.patient_in_time,
timestampdiff(minute,
t1.patient_in_time,
(select patient_in_time
from tbl
where patient_in_time > t1.patient_in_time
order by patient_in_time asc
limit 1)) diff
from tbl t1
order by patient_in_time
id | doctor_id | patient_in_time | diff
-: | --------: | :------------------ | ---:
1 | 2 | 2018-03-22 02:49:51 | 6
2 | 2 | 2018-03-22 02:56:37 | 3
3 | 2 | 2018-03-22 03:00:15 | 3
4 | 2 | 2018-03-22 03:03:37 | null
dbfiddle here
I have one table named Mydata as follows
id name type
--------------------------------------------
1 vinu 1
2 rinu 2
3 dilu 1
4 raju 2
5 manu 3
6 saju 3
7 ragu 3
8 sonu 1
9 sam 1
10 rag 1
--------------------------------------------
I want to print records with alternating type, for example:
First row with type =1
Second row with type =2
Third row with type =3
4th row type=1
5th row type=2 and so on
Required result as follows
id name type
-----------------------------------------
1 vinu 1
2 rinu 2
5 manu 3
3 dilu 1
4 raju 2
6 saju 3
8 sonu 1
7 ragu 3
9 sam 1
10 rag 1
----------------------------------------------
Sample data:
CREATE TABLE t
(`id` int, `name` varchar(4), `type` int)
;
INSERT INTO t
(`id`, `name`, `type`)
VALUES
(1, 'vinu', 1),
(2, 'rinu', 2),
(3, 'dilu', 1),
(4, 'raju', 2),
(5, 'manu', 3),
(6, 'saju', 3),
(7, 'ragu', 3),
(8, 'sonu', 1),
(9, 'sam', 1),
(10, 'rag', 1)
;
Query:
SELECT id, name, type FROM (
SELECT
t.*,
#rn := IF(#prev_type = type, #rn + 1, 1) AS rownumber,
#prev_type := type
FROM
t
, (SELECT #rn := 0, #prev_type := NULL) var_init_subquery
ORDER BY type
) sq
ORDER BY rownumber, type
Result:
| id | name | type |
|----|------|------|
| 1 | vinu | 1 |
| 4 | raju | 2 |
| 5 | manu | 3 |
| 9 | sam | 1 |
| 2 | rinu | 2 |
| 7 | ragu | 3 |
| 8 | sonu | 1 |
| 6 | saju | 3 |
| 10 | rag | 1 |
| 3 | dilu | 1 |
see it working live in an sqlfiddle
Caveat:
Don't expect this to be performant when you have lots of data. It's doing a full table scan.
Here's a manual entry to read when you're interested about how this variables work.
This cannot be done via a raw SQL query. Extract the rows you need to display, and then sort them via your application.
Alternatively... you could write a stored procedure, but I don't recommend this. You will need a temporary table and a cursor (that transparently creates another temporary table). Too much for a query that should be executed often.