I am trying to understand how this subquery works. The questions are as follows
Mary is a teacher in a middle school and she has a table seat storing students' names and their corresponding seat ids.The column id is continuous increment.
Mary wants to change seats for the adjacent students.
SELECT
(CASE
WHEN MOD(id, 2) != 0 AND counts != id THEN id + 1
WHEN MOD(id, 2) != 0 AND counts = id THEN id
ELSE id - 1
END) AS id,
student
FROM
seat,
(SELECT
COUNT(*) AS counts
FROM
seat) AS seat_counts
ORDER BY id ASC;
I am trying to understand the how the above query works. So in the CASE it checks if the id is odd or even and checks against the count to see if it is the last element. But how does the ORDER BY ASC work? Because for the first time it selects student Dorris and id 2. but then how is id 2 assigned to Abbot. Thanks.
SQL Table
id | student
1 | Abbot
2 | Doris
3 | Emerson
4 | Green
5 | Jeames
The Result will look like
id | student
1 | Dorris
2 | Abbot
3 | Green
4 | Emerson
5 | Jeames
OK what this is doing is the following -- if an id number is odd and it is not the max number then add one to it, otherwise subtract one from it.
It should be clear that would swap all but the last pair.
I think it is badly written I would write it like this:
WITH student_count(max) as
(
SELECT COUNT(*) FROM seat
)
SELECT
CASE
WHEN student_count.max != id AND MOD(id, 2) != 0 THEN id + 1
WHEN student_count.max != id AND MOD(id, 2) = 0 THEN id - 1
ELSE id
END AS id,
student
FROM seat
CROSS JOIN student_count
ORDER BY id ASC;
I would recommend you to check the results by removing ORDER BY statement. When you remove ORDER BY statement, result will be:
2 Abbot
1 Doris
4 Emerson
3 Green
5 Jeames
Which is completely right for your case. Basically, your query just alters id's values based on the CASE statement. When you add ORDER BY id ASC statement it just orders the result above.
select name,
case when mod(seat_id,2) = 1 and seat_id <> (select max(seat_id) from students) then seat_id + 1
when mod(seat_id,2)= 0 then seat_id - 1
when mod(seat_id,2) = 1 and seat_id = (select max(seat_id) from students) then seat_id
end swap
from students
SELECT
(CASE
WHEN MOD(id, 2) != 0 AND counts != id THEN id + 1
WHEN MOD(id, 2) != 0 AND counts = id THEN id
ELSE id - 1
END) AS id,
student
FROM
seat,
(SELECT
COUNT(*) AS counts
FROM
seat) AS seat_counts
ORDER BY id ASC;
Related
I'm trying to run an UPDATE query that uses the same table and I'm getting an error saying "1093 - Table 'queues_monitor_times' is specified twice, both as a target for 'UPDATE' and as a separate source for data".
UPDATE queues_monitor_times
SET queue_id = IF((
SELECT id
FROM queues_monitor_times
INNER JOIN(
SELECT pcc_group, pcc, gds, queue, category, `name`
FROM queues_monitor_times
GROUP BY pcc_group, pcc, gds, queue, category, `name`
HAVING COUNT(id) > 1
)temp ON queues_monitor_times.pcc_group = temp.pcc_group AND
queues_monitor_times.pcc = temp.pcc AND
queues_monitor_times.gds = temp.gds AND
queues_monitor_times.queue = temp.queue AND
queues_monitor_times.category = temp.category AND
queues_monitor_times.`name` = temp.`name`), 1, id)
WHERE
id NOT IN (SELECT MIN(id) FROM queues_old GROUP BY pcc_group, pcc, gds, queue, category, `name`);
I ran the select query by itself and it showed all the rows that were duplicates, which is what I wanted. I want queue_id to be set with the lowest duplicate row's id if the row is a duplicate or the row id if it is not.
Example of what the query should do:
id dup_id name value
1 1 John 13
2 2 John 13
3 3 Sally 6
4 4 Frank 4
5 5 Sally 6
And after running the query it will turn into
id dup_id name value
1 1 John 13
2 1 John 13
3 3 Sally 6
4 4 Frank 4
5 3 Sally 6
Please advise and thank you for your help.
I was able to solve my problem. Thanks for all your help!
UPDATE queues_monitor_times
SET queue_id = (
SELECT
id
FROM
queues_old
WHERE
queues_old.pcc_group = queues_monitor_times.pcc_group
AND queues_old.pcc = queues_monitor_times.pcc
AND queues_old.gds = queues_monitor_times.gds
AND queues_old.queue = queues_monitor_times.queue
AND queues_old.category = queues_monitor_times.category
AND queues_old.`name` = queues_monitor_times.`name`
GROUP BY pcc_group, pcc, gds, queue, category, `name`
HAVING COUNT(id) > 1)
WHERE
id NOT IN (SELECT MIN(id) FROM queues_old GROUP BY pcc_group, pcc, gds, queue, category, `name`);
For those that will want to use this in the future, queues_monitor_times table and queues_old table have the exact same data.
I am stuck in a problem explained below:
id | user_id | admin
1 | 1 | 2
2 | 1 | 5
3 | 2 | 5
4 | 2 | 5
5 | 3 | 5
6 | 4 | 5
I need to write a query to get all the users who are associated with an admin id passed in WHERE clause AND has more than one transactions(can be with another admin).
If user_id is 5 then result should come
user_id
1
2
Have tried
SELECT
user_id ,
COUNT(*) count
FROM
table
WHERE admin = 5
GROUP BY
user_id
Having
count > 1
but this above-mentioned query skips the user_id that has only one transaction with admin = 5 and has another row with another admin.
Suggestions?
Aggregate on the user_id and assert that:
The admin of interest (e.g. 5) appears,
Two or more admins of any type appear
SELECT user_id
FROM yourTable
GROUP BY user_id
HAVING SUM(CASE WHEN admin = 5 THEN 1 ELSE 0 END) > 0 AND
COUNT(*) > 1;
Demo
Try this
SELECT user_id from `tablename` WHERE admin IN( SELECT admin FROM `tablename` GROUP BY admin HAVING count(*) > 1)
In your query filter on admin=5 and with a separate subquery on those users, who have more than 1 transactions. You need to have the count in a subquery because the admin=5 criterion does not apply to the count.
SELECT DISTINCT user_id
FROM yourTable
WHERE admin_id=5
AND user_id IN (SELECT user_id FROM yourTable GROUP BY user_id HAVING COUNT(*)>1)
SELECT user_id
FROM test_table1
GROUP BY user_id
HAVING SUM(CASE WHEN admin = &n THEN 1 ELSE 0 END ) > 0
AND
COUNT(user_id) > 1;
If I understand your question, you want to ask:
Show me all users who have more than one admin associated
Also allow me to identify which admin specifically is in question (in your example 5)
This would give you a list of every user (user id only)
SELECT DISTINCT table.user_id
FROM table
JOIN (
SELECT user_id
,COUNT(DISTINCT admin_id) num_admins
FROM table
GROUP BY user_id
HAVING COUNT(DISTINCT admin_id) > 1
) multi_admin
ON table.user_id = multi_admin.user_id
WHERE admin = 5 -- if you want to look only at user_ids somehow associated with
So I have a student_profiles table and ranks table, I want to get the next rank based on the student rank. For example, I have rank 5 then the next rank will be rank 6. So this is my rank structure.
RANKS TABLE:
SELECT * FROM RANKS WHERE style_id = 1"
id style_id level name type primary_colour secondary_colour
1 1 1 Newbie double #4e90b2 #3aad04
22 1 2 Normal solid #fba729 NULL
31 1 3 Expert solid #4e805b NULL
and this is STUDENT_PROFILES TABLE
id | student_id | rank_id
------------------------------------
1 | 1 | 36
2 | 4 | 22
3 | 7 | 10
so all I have a variable is student_id, rank_id & style_id
so for example, I have this value student_id = 4, rank_id = 22 & style_id = 1
It should return
id style_id level name type primary_colour secondary_colour
31 | 1 | 3 | Expert | Solid | #4e805b | NULL
If you just want to get the second row:
Do it like this:
select * from
(select * from table order by id asc limit 2) as a order by id desc limit 1
Any query structure it will work as you need second row if you follow that script.
Try with that:
SELECT * FROM `ranks` WHERE `level` > (SELECT `level` FROM `ranks` WHERE `id` = rank_id) LIMIT 1
But I think it isn't very effective solution.
One option for getting the next highest level in the RANKS table is to self-join this table on the level column, order ascending, and retain the very first record only.
SELECT r2.*
FROM RANKS r1
INNER JOIN
STUDENT_PROFILES s1
ON r1.id = s1.rank_id
INNER JOIN
RANKS r2
ON r2.level > r1.level
ORDER BY r2.level
LIMIT 1
Demo here:
SQLFiddle
Note: If RANKS has duplicate levels, and you want the next level with regard to cardinality (i.e. you don't want a duplicate equal level returned), then my query could be slightly modified to filter out such duplicates.
Consider:
SELECT(count(c.id),
case when(count(c.id) = 0)
then 'loser'
when(count(c.id) BETWEEN 1 AND 4)
then 'almostaloser'
when(count(c.id) >= 5)
then 'notaloser'
end as status,
...
When all is said and done, the query as a whole produces a set of results that look similar to this:
Count | status
--------|-------------
2 | almostaloser //total count is between 2 and 4
--------|-------------
0 | loser // loser because total count = 0
--------|-------------
3 | almostaloser //again, total count between 2 and 4
--------|-------------
What I would like to achieve:
a method to reatain the information from the above table, but add a third column that will give a total count of each status, something like
select count(c.id)
case when(count(c.id) = 0 )
then loser as status AND count how many of the total count does this apply to
results would look similar to:
Count | status |total_of each status |
--------|-------------|---------------------|
2 | almostaloser| 2 |
--------|-------------|---------------------|
0 | loser | 1 |
--------|-------------|---------------------|
3 | almostaloser| 2 |
--------|-------------|----------------------
I've been told this could be achieved using a derived table, but i've not yet been able to get them both, only one or the other.
This can be achieved with this query (you must place your original query as subquery in two places):
SELECT t1.*, t2.total_of_each_status
FROM (
-- put here your query --
) t1
INNER JOIN (
SELECT status, count(*) AS total_of_each_status
FROM (
-- put here your query --
) t2
GROUP BY status
) t2 ON t2.status = t1.status
I have a table of winners vs losers (TABLE1) e.g.
+----+--------+-------+
| ID | Winner | Loser |
+----+--------+-------+
| 1 | 2 | 3 |
| 2 | 1 | 2 |
+----+--------+-------+
In the most recent game between Item 1 and Item 2, 1 won (ID 2). For this example, I'll refer to this as Current Winner and Current Loser.
I'm trying to build a query that works our inferences from past results.
e.g. if 2>3, and 1>2. Then I need to record a value for 1>3
The query I'm building would find multiple inferred losers against the current winner.
The ideal query would return an array of "losers", which I can loop through and record in the table as inferred results. In this case "3".
The table would be updated to:
+----+--------+-------+
| ID | Winner | Loser |
+----+--------+-------+
| 1 | 1 | 2 |
| 2 | 2 | 3 |
| 3 | 1 | 3 |
+----+--------+-------+
And if the query was run again, it would return nothing.
The process I have so far is:
Look up everything the Current Loser, has previously beaten (Previous losers to Current loser)
Check the table to see if any of the Previous Losers to Current Loser, has played the current winner, ever.
Any previous loser that has, should be removed
To get the list of things the Current Loser has beaten i use:
select * from TABLE1 where winner = 2
Then for the second bullet point, I've got two nested queries:
select * from TABLE1 where winner = 1 and loser = (select loser from rp_poss where winner = 2)
select * from TABLE1 where loser = 1 and winner = (select loser from rp_poss where winner = 2)
I really can't work out how to put these together, to remove the rows I don't want. Can somebody let me know what is best, and most efficient query for this for a example, a nested query, some kind of join? Pea brain is really struggling with this.
Thanks in advance
You can do it this way, by explicitly looking for certain records (a match between the two items) and counting to see if there are zero of them.
CURRENTLOSER and CURRENTWINNER are placeholders for variables or whatever.
select previous.loser
from table1 previous
where previous.winner=CURRENTLOSER and (
select count(*)
from table1 ancient
where (ancient.winner=CURRENTWINNER and ancient.loser=previous.loser) or
(ancient.loser=CURRENTWINNER and ancient.winner=previous.loser)
) = 0
Aliasing tables ("from table1 ancient") will help get the algorithm clear in your head.
This will get you one row for every person and competitor, and the last result with that competitor: (ie. if person 1 goes up against person 2 and loses, and then goes up against that person again and wins, this query will show person 1 with competitor 2 WIN, and person 2 with competitor 1 LOSE). It shows the LATEST result for each competitor, relative to the person.
http://sqlfiddle.com/#!2/823d3f/6/0
select x.person,
case when x.person <> t.winner then t.winner else t.loser end as competitor,
case when x.person = t.winner then 'WIN' else 'LOSE' end as result
from (select x.winner as person, max(y.id) as id
from (select winner from table1 union select loser from table1) x
join table1 y
on x.winner = y.winner
or x.winner = y.loser
group by x.winner) x
join table1 t
on x.person = t.winner
or x.person = t.loser
where x.id = t.id
The query below will insert inferred losers for the most recent match between 1 and 2 the first time it's run. The second time it won't insert any new rows.
Initially the not exists subquery had where id < current.id to remove previous losers, however, since inferred games are inserted with 'future' ids (i.e. 3 in your example), if you ran the query again, it would reinsert the rows, so I changed it to where id <> current.id, which means it will also exclude 'future' losers.
insert into mytable (winner, loser)
select current.winner, previous.loser
from (select id, winner, loser
from mytable where
(winner = 1 and loser = 2)
or (winner = 2 and loser = 1)
order by id desc limit 1) current
join mytable previous
on previous.winner = current.loser
and previous.id < current.id
where not exists (select 1 from mytable
where id <> current.id
and ((winner = current.winner and loser = previous.loser)
or (winner = previous.loser and loser = current.winner)))