Query's WHERE statement as a #variable - mysql

I have a query which I want to insert a variable into a WHERE statement.
WHERE
#Variable
I've tried the following (simplified) but it doesn't seem to work.
NOTE: I haven't included the concatenation element here trying to figure that part out myself before asking the question.
SET #id := x;
SET #n := (SELECT COUNT(*) FROM Table2 WHERE id=#id);
SET #Variable := (
(
Table1.Column1=(SELECT Column1 FROM Table2 WHERE id=#id LIMIT 1 OFFSET 0)
AND Table1.Column2=(SELECT Column2 FROM Table2 WHERE id=#id LIMIT 1 OFFSET 0)
AND Table1.Column3=(SELECT Column3 FROM Table2 WHERE id=#id LIMIT 1 OFFSET 0)
)
.........
OR
(
Table1.Column1=(SELECT Column1 FROM Table 2 WHERE id=#id LIMIT 1 OFFSET #n)
AND Table1.Column2=(SELECT Column2 FROM Table2 WHERE id=#id LIMIT 1 OFFSET #n)
AND Table1.Column3=(SELECT Column3 FROM Table2 WHERE id=#id LIMIT 1 OFFSET #n)
)
)
;
SELECT Table1.Column1, Table1.Column2, Table1.Column3, Table2.Column1, Table2.Column2, Table2.Column3
FROM Table1, Table2
WHERE
#Variable
;

So This:
SET #IDNumber := 21;
SELECT Players.PlayerID, COUNT(*) AS Games, SUM(Games.Points)
FROM Teams, Players, Games
WHERE
Teams.PlayerID=Players.PlayerID
AND
Games.Game=Teams.Game
AND
Games.Team=Teams.Team
AND
Games.GameDate=Teams.GameDate
AND
(
Games.Game= ANY (SELECT Game FROM Teams WHERE PlayerID=#IDNumber)
AND
Games.Team= ANY (SELECT Team FROM Teams WHERE PlayerID=#IDNumber)
AND
Games.GameDate= ANY (SELECT GameDate FROM Teams WHERE PlayerID=#IDNumber)
)
GROUP BY Teams.PlayerID
ORDER BY Games DESC
;
Creates This:
+----------+-------+-------------------+
| PlayerID | Games | SUM(Games.Points) |
+----------+-------+-------------------+
| 15 | 8 | 10 |
| 21 | 8 | 10 |
| 8 | 8 | 10 |
| 14 | 6 | 7 |
| 5 | 6 | 7 |
| 19 | 5 | 6 |
| 11 | 5 | 7 |
| 12 | 3 | 4 |
| 10 | 3 | 3 |
| 4 | 2 | 3 |
+----------+-------+-------------------+
10 rows in set (0.01 sec)
Which is exactly what I was looking for.

Related

MySQL - Select Top 5 with Rankings

I'm trying to get a users ranking getting his highest performances in every beatmap.
I get the user highest performance in every beatmap (only taking the top 5 performances) and adding them together, but it fails when the highest performance in one beatmap is repeated... because it counts twice
I'm based in this solution, but it doesn't works well for me...
Using MySQL 5.7
What i'm doing wrong?
Fiddle
Using this code:
SET group_concat_max_len := 1000000;
SELECT #i:=#i+1 rank, x.userID, x.totalperformance FROM (SELECT r.userID, SUM(r.performance) as totalperformance
FROM
(SELECT Rankings.*
FROM Rankings INNER JOIN (
SELECT userID, GROUP_CONCAT(performance ORDER BY performance DESC) grouped_performance
FROM Rankings
GROUP BY userID) group_max
ON Rankings.userID = group_max.userID
AND FIND_IN_SET(performance, grouped_performance) <= 5
ORDER BY
Rankings.userID, Rankings.performance DESC) as r
GROUP BY userID) x
JOIN
(SELECT #i:=0) vars
ORDER BY x.totalperformance DESC
Expected result:
+------+--------+------------------+
| rank | userID | totalperformance |
+------+--------+------------------+
| 1 | 1 | 450 |
+------+--------+------------------+
| 2 | 2 | 250 |
+------+--------+------------------+
| 3 | 5 | 140 |
+------+--------+------------------+
| 4 | 3 | 50 |
+------+--------+------------------+
| 5 | 75 | 10 |
+------+--------+------------------+
| 6 | 45 | 0 | --
+------+--------+------------------+
| 7 | 70 | 0 | ----> This order is not relevant
+------+--------+------------------+
| 8 | 76 | 0 | --
+------+--------+------------------+
Actual Result:
+------+--------+------------------+
| rank | userID | totalperformance |
+------+--------+------------------+
| 1 | 1 | 520 |
+------+--------+------------------+
| 2 | 2 | 350 |
+------+--------+------------------+
| 3 | 5 | 220 |
+------+--------+------------------+
| 4 | 3 | 100 |
+------+--------+------------------+
| 5 | 75 | 10 |
+------+--------+------------------+
| 6 | 45 | 0 | --
+------+--------+------------------+
| 7 | 70 | 0 | ----> This order is not relevant
+------+--------+------------------+
| 8 | 76 | 0 | --
+------+--------+------------------+
As you have mentioned that you are picking only top 5 performances per user across beatmaps then you can try this way:
select #i:=#i+1, userid,performance from (
select userid,sum(performance) as performance from (
select
#row_number := CASE WHEN #last_category <> t1.userID THEN 1 ELSE #row_number + 1 END AS row_number,
#last_category :=t1.userid,
t1.userid,
t1.beatmapid,
t1.performance
from (
select
userid, beatmapid,
max(performance) as performance
from Rankings
group by userid, beatmapid
) t1
CROSS JOIN (SELECT #row_number := 0, #last_category := null) t2
ORDER BY t1.userID , t1.performance desc
) t3
where row_number<=5
group by userid
)
t4 join (SELECT #i := 0 ) t5
order by performance desc
Above query will not consider duplicate Performance Score and pick only top 5 performance values.
DEMO

Get 10 latest records per category even some has less then 10

This is example of my table :
+-----+-----+------------+--------+-------------+--------------+
| LID | AID | Created | TypeID | PaymentDate | PaymentValue |
+-----+-----+------------+--------+-------------+--------------+
| 1 | 529 | 2017-05-12 | 1 | 2017-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 2 | 529 | 2018-04-10 | 4 | 2018-04-10 | 200 |
+-----+-----+------------+--------+-------------+--------------+
| 3 | 441 | 2014-01-23 | 3 | 2014-01-23 | 300 |
+-----+-----+------------+--------+-------------+--------------+
| 4 | 324 | 2017-09-14 | 1 | 2017-09-14 | 400 |
+-----+-----+------------+--------+-------------+--------------+
| 5 | 111 | 2018-05-12 | 0 | 2018-05-12 | 340 |
+-----+-----+------------+--------+-------------+--------------+
| 6 | 529 | 2018-05-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 7 | 529 | 2018-06-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 8 | 529 | 2018-07-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 9 | 529 | 2018-08-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 10 | 529 | 2018-09-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 11 | 529 | 2018-01-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 12 | 529 | 2018-05-14 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 13 | 529 | 2018-05-21 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
| 14 | 529 | 2018-03-12 | 1 | 2018-05-12 | 100 |
+-----+-----+------------+--------+-------------+--------------+
Here another table
+-----+-------+
| ID |caption|
+-----+-------+
| 0 | bad |
+-----+-------+
| 1 | good |
+-----+-------+
I need to get 10 latest records per AID. If there less than 10 records for some AID anyway i need to get ten rows and put "No payment date" into PaymentDate and Created fields, Null into TypeID and 0 into PaymentValue. I can get 10 or less latest records with
select *
from (select *,
(#rn := if(#c = AID, #rn + 1,
if(#c := AID, 1, 1)
)
) as rn
from history cross join
(select #rn := 0, #c := -1) params
order by AID, Created desc
) t
having rn <= 10;
But i dont know how force mysql to output 10 rows for each AID. Help me please.
Result should be in a form
AID,TypeId,Created,Caption
I have done it.
This query needs to create a row of 10 records to combine with distinct AID valies in the table. I was able to show the result for Amount and Create date and will leave it to you to continue since you will get the idea.
The critical part is to build a table with 10 rows times distinct AID so about 40 rows in table r. Then do a left join to table t which is similar to what you have done. Table t gets a rank of at most 10 records. Any missing rank up to 10 recs will be filled by table r. Coalesce will assign the default values such as 0 fro amount and 'no create date' for date.
http://sqlfiddle.com/#!9/855c21/2
SELECT coalesce(r.aid, t.aid) as aid,
coalesce(t.paymentvalue, 0) as paymentvalue,
coalesce(cast(t.created as char), 'no create date') as created
FROM (select * from (
select 1 as rw union
select 2 union select 3
union select 4 union select 5
union select 6 union select 7
union select 8 union select 9
union select 10) u
cross join (select distinct aid
from history) h
) as r
LEFT JOIN (
SELECT a.aid, a.paymentvalue,
a.created, count(*) rn
FROM history a
JOIN history b
ON a.aid = b.aid
AND a.created <= b.created
GROUP BY a.aid, a.created
HAVING COUNT(*) <= 10) t
on r.rw=t.rn and r.aid=t.aid
order by aid, created;
I have added RIGHT JOIN to bring in the null rows to top up to 10 (or n) rows per AID. Initially I use SELECT 1 UNION SELECT 2 ... to generate the 10 rows. In order to make it easier to increase the number of rows (say 100), I am trying this idea of generate_series equivalent for mysql. In order for this to work, the number of rows in history table must be equal to greater than the number of rows required per AID.
select t1.lid
,t2.aid
,coalesce(t1.created, "no created date") as created
,t1.typeID
,coalesce(t1.paymentdate, "no payment date") as paymentDate
,coalesce(t1.paymentvalue, 0) as paymentValue
,t2.rn
from
(
select *,
(#rn := if(#c = AID, #rn + 1,
if(#c := AID, 1, 1)
)
) as rn
from history cross join
(select #rn := 0, #c := -1) params
order by AID, Created desc
) t1
right join
( select *
from (select distinct aid from history ) h1
cross join
(select rn -- generate table with n rows numbered from 1 to n
from
(select
#num:= 0) init
cross join
(select #num := #num +1 rn
from history ) t -- assume history has at least 10 rows
limit
10 ) h2 -- n = 10; change it to the number of rows per aid required
) t2
on t1.aid = t2.aid and t1.rn = t2.rn
order by t2.aid, t2.rn

MySQL get ids of the range between two conditions

Let's say I've a table
+----+------------+
| id | condition |
+----+------------+
| 1 | open |
+----+------------+
| 2 | content |
+----+------------+
| 3 | content |
+----+------------+
| 4 | close |
+----+------------+
| 5 | nocontentx |
+----+------------+
| 6 | nocontenty |
+----+------------+
| 7 | open |
+----+------------+
| 8 | content |
+----+------------+
| 9 | close |
+----+------------+
| 10 | nocontentz |
+----+------------+
| 11 | open |
+----+------------+
| 12 | content |
+----+------------+
and want to get a new table where I get the IDs (the first and the last) of the values between "close" and "open". Note that the values between this two conditions are dynamic (I can't search by "nocontent"whatever)
Such as I get this table:
+----+----------+--------+
| id | start_id | end_id |
+----+----------+--------+
| 1 | 5 | 6 |
+----+----------+--------+
| 2 | 10 | 10 |
+----+----------+--------+
Thanks in advance!
http://sqlfiddle.com/#!2/c255c8/2
You can do this using a correlated subquery:
select (#rn := #rn + 1) as id,
id as startid,
(select id
from atable a2
where a2.id > a.id and
a2.condition = 'close'
order by a2.id asc
limit 1
) as end_id
from atable a cross join
(select #rn := 0) vars
where a.condition = 'open';
The working SQL Fiddle is here.
Note this returns the third open as well. If you don't want it, then add having end_id is not null to the end of the query.
EDIT:
If you know the ids are sequential, you can just add and subtract 1 from the above query:
select (#rn := #rn + 1) as id,
id+1 as startid,
(select id
from atable a2
where a2.id > a.id and
a2.condition = 'open'
order by a2.id asc
limit 1
) - 1 as end_id
from atable a cross join
(select #rn := 0) vars
where a.condition = 'close';
You can also do this in a different way, which is by counting the number of open and closes before any given row and using this as a group identifier. The way your data is structured, every other group is what you are looking for:
select grp, min(id), max(id)
from (select t.*,
(select sum(t2.condition in ('open', 'close'))
from t t2
where t2.id <= t.id
) as grp
from t
) t
where t.condition not in ('open', 'close') and
grp % 2 = 0
group by grp;

MySQL - Check if consecutive columns are the same and display only those rows

I have an events table that contains IDs (id) and dates (eventDate) corresponding to those IDs (id and eventDate are not the only columns in the table).
SQLFiddle here.
+--------+----+---------------------+
| row_id | id | eventDate |
+--------+----+---------------------+
| 1 | 1 | 2014-02-27 23:19:41 |
| 2 | 1 | 2014-02-27 23:21:41 |
| 3 | 1 | 2014-02-27 23:21:41 |
| 4 | 2 | 2014-02-27 23:23:08 |
| 5 | 2 | 2014-02-27 23:25:08 |
| 6 | 2 | 2014-02-27 23:25:08 |
| 9 | 3 | 2014-02-28 15:36:55 |
| 8 | 3 | 2014-02-28 15:36:55 |
| 7 | 3 | 2014-02-28 15:34:55 |
| 10 | 4 | 2014-02-28 19:31:31 |
| 11 | 4 | 2014-02-28 19:33:31 |
| 12 | 4 | 2014-02-28 19:33:31 |
| 13 | 5 | 2014-02-28 19:33:34 |
| 14 | 5 | 2014-02-28 19:33:33 |
| 15 | 5 | 2014-02-28 19:31:33 |
| 16 | 6 | 2014-03-04 22:40:21 |
| 17 | 6 | 2014-03-04 22:38:21 |
| 18 | 6 | 2014-03-04 22:40:21 |
| 19 | 7 | 2014-03-04 23:08:37 |
| 20 | 7 | 2014-03-04 23:08:38 |
+--------+----+---------------------+
I want to select only those rows from the table, where consecutive event dates are the same for the same ID.
Thus, I would like to see only these entries -
+----+---------------------+
| id | eventDate |
+----+---------------------+
| 1 | 2014-02-27 23:21:41 |
| 1 | 2014-02-27 23:21:41 |
| 2 | 2014-02-27 23:25:08 |
| 2 | 2014-02-27 23:25:08 |
| 3 | 2014-02-28 15:36:55 |
| 3 | 2014-02-28 15:36:55 |
| 4 | 2014-02-28 19:33:31 |
| 4 | 2014-02-28 19:33:31 |
Note that there is no
| 6 | 2014-03-04 22:40:21 |
| 6 | 2014-03-04 22:40:21 |
in the above result, because they're not consecutive.
I know I can store the output of the SQL query in a file and then use a unix tool to do this, but I want to know if this is achievable directly through SQL.
Should be able to accomplish this leveraging a group by although my mySql is a bit rusty.
SELECT t.*
FROM (
SELECT
id,
eventDate,
COUNT(0) AS numRows
FROM tabl
GROUP BY id, DATE(eventDate)
HAVING COUNT(0) > 1
ORDER BY eventDate
) t
Then you just join this correlated sub query back to the original table if you need additional columns.
select id,eventDate from your_tableName where eventDate in (select eventDate from your_tableName group by id,eventDate having count(eventDate) > 1);
select ta.id, ta.eventDate from
(
select row_id as ra, t1.id, t1.eventDate
from events t1
) as ta
join
(
select row_id as rb, t2.id, t2.eventDate
from events t2
) as tb
on rb = ra+1 and ta.id = tb.id and ta.eventDate = tb.eventDate
I have found a way to match the eventDate of the next row but the only drawback is that it will return the number of consecutive dates - 1 rows. But in your code you can just loop 1 extra time.
SET #inc = 0;
SET #innerInc = 1;
SELECT t1.id, t1.eventDate
FROM (
SELECT id, eventDate, (#inc := #inc + 1) as increment FROM temp
) t1
WHERE t1.eventDate = (
SELECT t2.eventDate FROM (
SELECT eventDate, (#innerInc := #innerInc + 1) as increment FROM temp
) t2
WHERE t2.increment = t1.increment
);
Here is the SQLFiddle for this: Here
This should be able to do it with a single table scan (no subqueries,joins,etc..)
SELECT t.id,t.eventDate
FROM (
SELECT
IF(id = #prevID AND eventDate = #prevDate, #counter, #counter := #counter+1) as c,
#prevID := id as id,
#prevDate := eventDate as eventDate
FROM events e
JOIN (SELECT #counter := 0, #prevID := NULL, #prevDate := NULL) as stuff
WHERE 1 #or some where condition for events
ORDER BY row_id ASC
) as t
GROUP BY t.c
If it's not specifically that you need the positionally consecutive entry, but rather that if you grouped by eventDate and found entries with the same eventDate then you'd want those records, then the following:
select *
from Table a
join (select eventDate, count(*)
from Table
group by eventDate
having count(*) > 1) b
on (a.eventDate = b.eventDate)
The arbitrary dependence on the position of the data suggests that there is some other property you're not sharing, and it is by that property that the records are retrieved and ordered. If such a property determines the record's position, then it's precisely by ordering or grouping with that property that you can efficiently solve this.
After throwing away my self-join I think you're going to have to generate row_numbers for each sub_query:
select #rn1 := #rn1+1 as ra, t1.id, t1.eventDate
from events t1
join (select #rn1 := 0) r;
and then join that to
select #rn2 := #rn2+1 as rb, t2.id, t2.eventDate
from events t2
join (SELECT #rn2 := 0) r;
so final answer:
select ta.id, ta.eventDate from
(
select #rn1 := #rn1+1 as ra, t1.id, t1.eventDate
from events t1
join (select #rn1 := 0) r
) as ta
join
(
select #rn2 := #rn2+1 as rb, t2.id, t2.eventDate
from events t2
join (SELECT #rn2 := 0) r
) as tb
on rb = ra+1 and ta.id = tb.id and ta.eventDate = tb.eventDate
Results:
1 February, 27 2014 23:21:41+0000
2 February, 27 2014 23:25:08+0000
3 February, 28 2014 15:36:55+0000
4 February, 28 2014 19:33:31+0000

mysql select only X number of diffent Y row values

I have a table that I'm selecting from in which I only want results for 2 differt column values... Here is what I mean data wise...
some_table
+----+----------+-------------+
| id | some_id | some_column |
+----+----------+-------------+
| 1 | 10 | alpha |
| 2 | 10 | alpha |
| 3 | 10 | alpha |
| 4 | 20 | alpha |
| 5 | 30 | alpha |
+----+----------+-------------+
An example of the type of query I'm running is:
SELECT * FROM some_table WHERE some_column = `alpha`;
How do I modify that select so that it only gives me results for up to 2 diffent some_id's... an example result is:
some_table
+----+----------+-------------+
| id | some_id | some_column |
+----+----------+-------------+
| 1 | 10 | alpha |
| 2 | 10 | alpha |
| 3 | 10 | alpha |
| 4 | 20 | alpha |
+----+----------+-------------+
It would not include id = 5 row because we only grab results for up to 2 different some_id's (10, 20 in this case).
Actually figured it out on my own, just needed to use a JOIN / SELECT DISTINCT combo. Here is the correct query...
SELECT * FROM some_table s1 JOIN (SELECT DISTINCT some_id FROM s1 LIMIT 2) s2 ON s1.some_id = s2.some_id;
Possibly use a subselect to get the first 2 ids, and then inner join that against your table
SELECT a.id, a.some_id, a.some_column
FROM some_table a
INNER JOIN
(
SELECT DISTINCT some_id
FROM some_table
ORDER BY some_id
LIMIT 2
) b
ON a.some_id = b.some_id
I am not sure if this is the optimal solution but it should do the trick:
SET #firstId:=(select distinct some_id from some_table limit 1) ;
SET #secondId:=(select distinct some_id from some_table limit 1,1) ;
SELECT *
FROM some_table
WHERE some_column="alpha"
AND some_id IN (#firstId, #secondId);