How to get how many chapters read by user in mysql - mysql

I am having three tables bible_chapters, bible_reading_portions, user_bible_trackings, 1 chapter may have multiple bible portions and we are tracking portions in user_bible_trackings table. Below is my schema:-
bible_chapters
id chapter_name
1 Chpt1
2 Chpt2
3 Chpt3
bible_reading_portions
id bible_chapter_id portion_name
1 1 Chp1P1
2 2 Chp2P1
3 2 Chp2P2
4 2 Chp2P3
5 3 Chp3P1
user_bible_trackings
id bible_reading_portion_id user_id
1 1 1
2 2 1
3 3 1
4 4 1
5 1 1
6 2 1
7 3 1
8 4 1
9 1 1
10 2 1
So you see that above user_bible_trackings table user_id 1 have read Chpt1 3 times and Chpt2 2 times and user again started the chapter2 3rd time but its not complete yet beacuse chapt2 belongs to 3 portions,.... for the 3rd time user not read all the portions. So my expected output be like:-
user_id total_chapteres_read
1 5 // i.e means user read chpt1 3 times and chpt2 2 times so count will be 3+2=5
Can anyone help me how can i acheieve the same.
#Akina PLease check i have added the schema queries below:-
CREATE TABLE `bible_chapters` (
`id` int(11) NOT NULL,
`chapter_name` varchar(150) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `bible_chapters` (`id`, `chapter_name`) VALUES
(1, 'Chp1'),
(2, 'Chp2'),
(3, 'Chp3');
---------------------
CREATE TABLE `bible_reading_portions` (
`id` int(11) NOT NULL,
`bible_chapter_id` int(11) NOT NULL,
`portion_name` varchar(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `bible_reading_portions` (`id`, `bible_chapter_id`, `portion_name`)
VALUES
(1, 1, 'Chp1p1'),
(2, 2, 'Chp2p2'),
(3, 2, 'Chp2p2'),
(4, 2, 'Chp2p3');
-----------
CREATE TABLE `user_bible_trackings` (
`id` int(11) NOT NULL,
`bible_reading_portion_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `user_bible_trackings` (`id`, `bible_reading_portion_id`, `user_id`) VALUES
(1, 1, 1),
(2, 2, 1),
(3, 3, 1),
(4, 4, 1),
(5, 1, 1),
(6, 2, 1),
(7, 3, 1),
(8, 4, 1),
(9, 1, 1),
(10, 2, 1);

WITH
cte1 AS (
SELECT ubt.user_id,
bc.id bc_id, bc.chapter_name,
brp.id brp_id, brp.portion_name,
COUNT(*) cnt
FROM bible_chapters bc
JOIN bible_reading_portions brp ON bc.id = brp.bible_chapter_id
JOIN user_bible_trackings ubt ON brp.id = ubt.bible_reading_portion_id
GROUP BY 1,2,3,4,5
),
cte2 AS (
SELECT user_id, bc_id,
COUNT(brp_id) portions_count,
MIN(cnt) complete_readings
FROM cte1
GROUP BY 1,2
)
SELECT user_id, SUM(complete_readings) total_readings
FROM cte2
GROUP BY 1
fiddle with some comments.
The solution does not check does all portions of a chapter were read. For to take this into account you must compare portions_count (added but not used) with the same value for a chapter (obtained in separate CTE).

Related

SQL - Get count of phone calls that resulted in meeting

I have a table named 'reports', something like this:
id
user_id
type
customer_id
text
created_at
1
1
1
3
....
2021-08-07 17:00:52
2
1
1
3
....
2021-08-12 10:11:11
3
1
1
9
....
2021-08-12 10:08:14
4
1
2
3
....
2021-08-12 10:04:08
5
1
2
9
....
2021-08-13 20:32:21
6
2
1
7
....
2021-08-13 20:34:17
7
2
2
8
....
2021-08-14 18:55:09
I want to get the count of rows that a user has submitted type 1 reports that result in a type 2 report.
Type 1 report means reporting a phone call to the customer and type 2 means meeting the customer. I want to receive the number of calls that resulted in an meeting.
For example, for user 1 should returns 3, because for customer 3, IDs 1 and 2 have led to ID 4, and for customer 9, ID 3 has led to ID 5.
But for user 2, ID 7 is type 2 but there is no previous type 1 report for customer 8, so it returns 0.
Schema (MySQL v5.7)
CREATE TABLE reports
(id int auto_increment primary key,
user_id int,
type int,
customer_id int,
text varchar(4),
created_at varchar(19)
);
INSERT INTO reports
(id, user_id, type, customer_id, text, created_at)
VALUES
(1, 1, 1, 3, '....', '2021-08-07 17:00:52'),
(2, 1, 1, 3, '....', '2021-08-12 10:11:11'),
(3, 1, 1, 9, '....', '2021-08-12 10:08:14'),
(4, 1, 2, 3, '....', '2021-08-12 10:04:08'),
(5, 1, 2, 9, '....', '2021-08-13 20:32:21'),
(6, 2, 1, 7, '....', '2021-08-13 20:34:17'),
(7, 2, 2, 8, '....', '2021-08-14 18:55:09');
Query #1
SELECT x.user_id
, COUNT(DISTINCT y.id) total
FROM reports x
LEFT
JOIN reports y
ON y.id<=x.id
AND y.user_id = x.user_id
AND y.customer_id = x.customer_id
AND y.type = 1
WHERE x.type = 2
GROUP
BY x.user_id;
user_id
total
1
3
2
0
View on DB Fiddle
You haven't shown how your are expecting results, but simply refer to User 1 and 2 and the total votes, you can try using a lateral join
select user_id, Coalesce(Sum(valid),0) rowcount
from reports r
join lateral (
select
case when exists (select * from reports rr
where rr.user_id=r.user_Id and rr.type=2 and rr.customer_id=r.customer_Id and r.type=1)
then 1 end valid
)x
group by User_Id
Example Fiddle
First you Count the number of customers that comply with your criterioa type and typoe 2. But ti get all user you need to join the list of users
I used LEFT join as also RIGHt JOIN in my example
CREATE TABLE reports
(`id` int, `user_id` int, `type` int, `customer_id` int, `text` varchar(4), `created_at` varchar(19))
;
INSERT INTO reports
(`id`, `user_id`, `type`, `customer_id`, `text`, `created_at`)
VALUES
(1, 1, 1, 3, '....', '2021-08-07 17:00:52'),
(2, 1, 1, 3, '....', '2021-08-12 10:11:11'),
(3, 1, 1, 9, '....', '2021-08-12 10:08:14'),
(4, 1, 2, 3, '....', '2021-08-12 10:04:08'),
(5, 1, 2, 9, '....', '2021-08-13 20:32:21'),
(6, 2, 1, 7, '....', '2021-08-13 20:34:17'),
(7, 2, 2, 8, '....', '2021-08-14 18:55:09')
;
SELECT IFNULL(Counts,0),t1.user_id
FROM (SELECT COUNT(distinct r.customer_id) counts,user_id
FROM reports r
WHERE type = 1 AND EXISTS(SELECT 1 FROM reports WHERE type = 2 AND customer_id = r.customer_id)
GROUP BY user_id) r RIGHT JOIN (SELECT DISTINCT user_id FROm reports) t1 ON r.user_id = t1.user_id
IFNULL(Counts,0) | user_id
---------------: | ------:
2 | 1
0 | 2
SELECT IFNULL(Counts,0),t1.user_id
FROM (SELECT DISTINCT user_id FROm reports) t1 LEFT JOIN (SELECT COUNT(distinct r.customer_id) counts,user_id
FROM reports r
WHERE type = 1 AND EXISTS(SELECT 1 FROM reports WHERE type = 2 AND customer_id = r.customer_id)
GROUP BY user_id) r ON r.user_id = t1.user_id
IFNULL(Counts,0) | user_id
---------------: | ------:
2 | 1
0 | 2
db<>fiddle here

MySQL joining tables of different structure

I am having trouble either getting any result or a correct result in the following problem - http://www.sqlfiddle.com/#!9/696ed2/4
Overall goal is to list all transactions of users who are linked together as 'Customers'. So if John is looking at his dashboard, he will see which books Alice (his customer) has rented (including book title), and which books were sold (he won't be able to see the title of that book).
When two multiple tables are joined to the parent table, where both depending tables have an 'active' flag set against each row, I can't seem to get only active rows.
# USERS
CREATE TABLE `users` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`active` boolean DEFAULT NULL
);
INSERT INTO `users` (`id`, `name`, `active`) VALUES
(1, 'John', 1),
(2, 'Alice', 1),
(3, 'Jess', 1),
(4, 'Bob', 1);
# BOOKS
CREATE TABLE `books` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`active` boolean DEFAULT NULL
);
INSERT INTO `books` (`id`, `name`, `active`) VALUES
(1, 'On the Road', 1),
(2, 'Neuromancer', 0),
(3, 'Modern History', 1),
(4, 'Red Mars', 1);
# TRANSACTIONS
CREATE TABLE `transactions` (
`id` int(11) NOT NULL,
`user_1_id` int(11) NOT NULL,
`user_2_id` int(11) DEFAULT NULL,
`book_id` int(11) DEFAULT NULL,
`timestamp` varchar(20) DEFAULT NULL,
`type` enum('Rent', 'Sold') NOT NULL
);
INSERT INTO `transactions` (`id`, `user_1_id`, `user_2_id`, `book_id`, `timestamp`, `type`) VALUES
(1, 1, 2, 1, '1465238591', 'Rent'),
(2, 2, 1, 2, '1465238592', 'Rent'),
(3, 2, 3, 3, '1465238593', 'Rent'),
(4, 3, 4, NULL, '1465238594', 'Sold'),
(5, 2, 3, NULL, '1465238595', 'Sold'),
(6, 3, 4, NULL, '1465238596', 'Sold'),
(7, 2, 2, 4, '1465238597', 'Rent'),
(8, 1, 3, 1, '1465238598', 'Rent'),
(9, 2, 4, 2, '1465238598', 'Rent');
# RELATIONSHIPS
CREATE TABLE `relationships` (
`id` int(11) NOT NULL,
`user_1_id` int(11) DEFAULT NULL,
`user_2_id` int(11) NOT NULL,
`type` enum('Customer', 'Supplier') NOT NULL
);
INSERT INTO `relationships` (`id`, `user_1_id`, `user_2_id`, `type`) VALUES
(1, 1, 2, 'Customer'),
(2, 2, 1, 'Customer'),
(3, 2, 4, 'Customer'),
(3, 1, 4, 'Supplier'),
(3, 3, 1, 'Customer');
Query:
SELECT DISTINCT
t.id,
t.type,
t.timestamp,
u1.name as user_1_name,
u2.name as user_2_name,
b.name as book_name
FROM transactions t
LEFT JOIN relationships r ON (r.user_1_id = 1 AND r.type = 'Customer')
LEFT JOIN books b ON (b.id = t.book_id AND b.active)
LEFT JOIN users u1 ON (u1.id = t.user_1_id) # AND u1.active
LEFT JOIN users u2 ON (u2.id = t.user_2_id) # AND u2.active
WHERE (r.user_2_id = t.user_1_id OR t.user_2_id = 1 AND t.user_1_id != 1)
# AND b.active AND u1.active AND u2.active
[Result]:
| id | type | timestamp | user_1_name | user_2_name | book_name |
|----|------|------------|-------------|-------------|----------------|
| 3 | Rent | 1465238593 | Alice | Jess | Modern History |
| 2 | Rent | 1465238592 | Alice | John | (null) | <<< Should not be here
| 7 | Rent | 1465238597 | Alice | Alice | Red Mars |
| 5 | Sold | 1465238595 | Alice | Jess | (null) | <<< Correct
| 9 | Rent | 1465238598 | Alice | Bob | (null) | <<< Should not be here
In the above result, the problem is that book Neuromancer has flag 'active' set to 0, so should not appear in the result. I have played with placing AND b.active at different places, but the results are always wrong. (See http://www.sqlfiddle.com/#!9/696ed2/5)
Looking at the mess above, I am not even sure my approach is any good, any suggestions are welcome.
As D. Smania mentioned in comments you need to make sure b.active is either 1 or NULL but based on your LEFT JOIN condition b.active will always be 1 so you need to do the join only on id and rely on the WHERE condition for comparison. This should yield the results you asked for:
SELECT DISTINCT
t.id,
t.type,
t.timestamp,
u1.name AS user_1_name,
u2.name AS user_2_name,
b.name AS book_name
FROM transactions t
LEFT JOIN relationships r ON (r.user_1_id = 1 AND r.type = 'Customer')
LEFT JOIN books b ON (b.id = t.book_id)
LEFT JOIN users u1 ON (u1.id = t.user_1_id)
LEFT JOIN users u2 ON (u2.id = t.user_2_id)
WHERE (r.user_2_id = t.user_1_id OR t.user_2_id = 1 AND t.user_1_id != 1)
AND (b.active OR b.active IS NULL)
AND u1.active AND u2.active
SQL Fiddle
One note - in your first WHERE condition it's not clear to me whether you mean:
(r.user_2_id = t.user_1_id OR (t.user_2_id = 1 AND t.user_1_id != 1))
or
((r.user_2_id = t.user_1_id OR t.user_2_id = 1) AND t.user_1_id != 1)
It's always best to be explicit with your logic grouping when you have adjacent AND and OR conditions.

How to join two mysql table and retrieve latest results from joined table?

I have two tables. I need to join these two tables and retrieve latest status from execution table. How can I retrieve?
My schema and data:
CREATE TABLE test
(`id` serial primary key, `ref_id` int, `ref_name` varchar(7))
;
INSERT INTO test
(`id`, `ref_id`, `ref_name`)
VALUES
(1, 1, 'trial'),
(2, 3, 'test'),
(3, 7, 'testing')
;
CREATE TABLE execution
(`id` serial primary key, `ref_id` int, `status` varchar(11))
;
INSERT INTO execution
(`id`, `ref_id`, `status`)
VALUES
(1, 1, 'Completed'),
(2, 2, 'Completed'),
(3, 1, 'Completed'),
(4, 3, 'In progress'),
(5, 3, 'To do'),
(6, 2, 'In progress'),
(7, 1, 'Completed'),
(7, 1, 'To do')
;
Expected result is here below.
ref_id | ref_name | status |
3 | testing | In progress |
2 | test | To do |
1 | trial | To do |
I have tried with below query:
SELECT
ref_id,
ref_name,
status
FROM
test
JOIN execution ON test.ref_id = execution.ref_id
GROUP BY `ref_id`
ORDER BY `ref_id` DESC;
This query retrieves the status, but the retrieved status is not a latest one. How can retrieve the latest status by joining these two tables.
you can use below query
select T2.ref_id,T2.ref_name,OE.status from
(
select t1.ref_id,t1.ref_name,e.id from test t1 inner join
(select max(id) as id,ref_id from execution group by ref_id) as e
on
t1.ref_id=e.ref_id
) as T2
inner join execution OE on T2.id=OE.id
https://www.db-fiddle.com/f/rvnm8APX27dmW9a84JkCsS/1
It seems you have given in-correct data as an example as ref_id 7 not found in
execution table. However this might help you
SELECT b.ref_id,
b.ref_name,
a.status
FROM execution a
JOIN (SELECT MAX(id) id ,ref_id
FROM execution
GROUP BY ref_id) a1
USING(id,ref_id)
JOIN test b ON a.ref_id = b.ref_id ORDER BY ref_id DESC;

MYSQL: select the top N candidates

I have the following DB scheme for university elections:
for each department, I have the following positions:
1 CHEF (which is the candidate_position = 1)
& 6 Members (which is the candidate_position = 2)
I want to obtain the winners of the election in each department.
to obtain the winner of CHEF position in "Informatique" department, I did the following query:
SELECT doctor.firstname, doctor.lastname, votes
FROM (SELECT COUNT(*) AS votes FROM candidate_votes WHERE candidate_votes.candidate_position = 1 GROUP BY candidate_votes.candidate_id) AS votes, doctor
INNER JOIN department_candidates ON department_candidates.doctor_id = doctor.id
INNER JOIN department ON department.id = department_candidates.department_id AND department.name = 'Informatique'
INNER JOIN candidate_votes ON candidate_votes.candidate_id = doctor.id AND candidate_votes.candidate_position = 1
GROUP BY candidates_votes.candidate_id
please note I didn't use LIMIT 1 because may be there is a tie (or draw) of votes between multiple candidates
based on the results, I think that my query of selecting the winner of Chef position is right, But I want some help to know how to select the first 6 candidates of Member position ?
Data set:
--
-- Table structure for table `candidate_votes`
--
DROP TABLE IF EXISTS `candidate_votes`;
CREATE TABLE IF NOT EXISTS `candidate_votes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`candidate_id` int(11) NOT NULL,
`voter_id` int(11) NOT NULL,
`candidate_position` tinyint(1) NOT NULL COMMENT '1: chef, 2: member',
`date` date NOT NULL,
PRIMARY KEY (`id`),
KEY `fk-candidate_votes-voter_id` (`voter_id`),
KEY `fk-candidate_votes-candidate_id_idx` (`candidate_id`)
) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8;
--
-- Dumping data for table `candidate_votes`
--
INSERT INTO `candidate_votes` (`id`, `candidate_id`, `voter_id`, `candidate_position`, `date`) VALUES
(24, 2, 1, 1, '2018-05-26'),
(25, 1, 1, 2, '2018-05-26'),
(26, 6, 1, 2, '2018-05-26'),
(27, 5, 1, 2, '2018-05-26'),
(28, 7, 1, 2, '2018-05-26'),
(29, 8, 1, 2, '2018-05-26'),
(30, 9, 1, 2, '2018-05-26'),
(31, 2, 2, 1, '2018-05-16'),
(32, 3, 7, 1, '2018-05-22'),
(33, 3, 8, 1, '2018-05-22'),
(34, 4, 6, 2, '2018-05-29'),
(35, 7, 6, 2, '2018-05-29');
-- --------------------------------------------------------
--
-- Table structure for table `department`
--
DROP TABLE IF EXISTS `department`;
CREATE TABLE IF NOT EXISTS `department` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `department-name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
--
-- Dumping data for table `department`
--
INSERT INTO `department` (`id`, `name`) VALUES
(1, 'Informatique'),
(2, 'Mathematique'),
(4, 'physique');
-- --------------------------------------------------------
--
-- Table structure for table `department_candidates`
--
DROP TABLE IF EXISTS `department_candidates`;
CREATE TABLE IF NOT EXISTS `department_candidates` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`department_id` int(11) NOT NULL,
`doctor_id` int(11) NOT NULL,
`candidate_position` tinyint(1) NOT NULL COMMENT '1: chef, 2: member',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;
--
-- Dumping data for table `department_candidates`
--
INSERT INTO `department_candidates` (`id`, `department_id`, `doctor_id`, `candidate_position`) VALUES
(5, 1, 3, 1),
(7, 1, 4, 2),
(8, 1, 1, 2),
(9, 1, 2, 1),
(10, 1, 6, 2),
(11, 1, 5, 2),
(12, 1, 7, 2),
(13, 1, 8, 2),
(14, 1, 9, 2);
-- --------------------------------------------------------
--
-- Table structure for table `doctor`
--
DROP TABLE IF EXISTS `doctor`;
CREATE TABLE IF NOT EXISTS `doctor` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`firstname` varchar(255) NOT NULL,
`lastname` varchar(255) NOT NULL,
`department_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
--
-- Dumping data for table `doctor`
--
INSERT INTO `doctor` (`id`, `firstname`, `lastname`, `department_id`) VALUES
(1, 'doc1_fn', 'doc1_ln', 1),
(2, 'doc2_fn', 'doc2_ln', 1),
(3, 'doc3_fn', 'doc3_ln', 1),
(4, 'doc4_fn', 'doc4_ln', 1),
(5, 'doc5_fn', 'doc5_ln', 1),
(6, 'doc6_fn', 'doc6_ln', 1),
(7, 'doc7_fn', 'doc7_ln', 1),
(8, 'doc8_fn', 'doc8_ln', 1),
(9, 'doc9_fn', 'doc9_ln', 1);
-- --------------------------------------------------------
Sqlfiddle DEMO
Consider the following:
SELECT x.*
, CASE WHEN #prev_position = candidate_position THEN CASE WHEN #prev_total = total THEN #i:=#i ELSE #i:=#i+1 END ELSE #i:=1 END i
, #prev_position := candidate_position prev_position
, #prev_total := total prev_total
FROM
(
SELECT candidate_id
, candidate_position
, COUNT(*) total
FROM candidate_votes
GROUP
BY candidate_id
, candidate_position
) x
JOIN
( SELECT #prev_position := null,#prev_total:=null,#i:=0) vars
ORDER
BY candidate_position
, total DESC;
+--------------+--------------------+-------+------+---------------+------------+
| candidate_id | candidate_position | total | i | prev_position | prev_total |
+--------------+--------------------+-------+------+---------------+------------+
| 2 | 1 | 2 | 1 | 1 | 2 |
| 3 | 1 | 2 | 1 | 1 | 2 |
| 7 | 2 | 2 | 1 | 2 | 2 |
| 8 | 2 | 1 | 2 | 2 | 1 |
| 9 | 2 | 1 | 2 | 2 | 1 |
| 1 | 2 | 1 | 2 | 2 | 1 |
| 4 | 2 | 1 | 2 | 2 | 1 |
| 5 | 2 | 1 | 2 | 2 | 1 |
| 6 | 2 | 1 | 2 | 2 | 1 |
+--------------+--------------------+-------+------+---------------+------------+
In this example, i represents rank. For position 1, we can see that two candidates tied for first place. For position 2, there was one outright winner, with all remaining candidates tying for second place.
Apparently I was trying to be too clever in my other answer, you can achieve a simple ranking table like this:
SELECT cast(dc.candidate_position AS UNSIGNED) AS position, dc.doctor_id, doc.firstname, doc.lastname, v.votes
FROM department_candidates dc
JOIN department dept ON dept.id=dc.department_id AND dept.name='Informatique'
JOIN doctor doc ON doc.id=dc.doctor_id
JOIN (SELECT candidate_position AS cp, candidate_id AS cid, count(candidate_id) AS votes
FROM candidate_votes
GROUP BY cid) v
ON v.cid=doc.id AND v.cp = dc.candidate_position
ORDER BY position, v.votes DESC
Output:
position doctor_id firstname lastname votes
1 3 doc3_fn doc3_ln 2
1 2 doc2_fn doc2_ln 2
2 7 doc7_fn doc7_ln 2
2 4 doc4_fn doc4_ln 1
2 1 doc1_fn doc1_ln 1
2 6 doc6_fn doc6_ln 1
2 5 doc5_fn doc5_ln 1
2 8 doc8_fn doc8_ln 1
2 9 doc9_fn doc9_ln 1
Demo
This query will give you the number of votes required to be a winner for a given position (note I have parameterised it with variables):
SET #position = 2;
SET #numwinners = 3;
SELECT #rank := #rank+1 AS rank, votes
FROM (SELECT COUNT(candidate_id) AS votes
FROM candidate_votes
WHERE candidate_position = #position
GROUP BY candidate_id
ORDER BY votes DESC) vr
JOIN (select #rank := 0) r
GROUP BY rank
HAVING rank = #numwinners
Output for your data with #position=2 and #numwinners=3:
rank votes
3 1
Having figured out how many votes are required to be a winner, we just need to find all candidates that have the required number of votes. Since the results are based on number of votes, ties are automatically taken care of. This query will generate that output:
SET #position = 2;
SET #numwinners = 3;
SELECT doc.firstname, doc.lastname, v.votes
FROM department_candidates dc
JOIN department dept ON dept.id=dc.department_id AND dept.name='Informatique'
JOIN doctor doc ON doc.id=dc.doctor_id
JOIN (SELECT candidate_id AS cid, count(candidate_id) AS votes
FROM candidate_votes
WHERE candidate_position = #position
GROUP BY cid) v
ON v.cid=dc.id
WHERE v.votes >= (SELECT votes
FROM (SELECT #rank := #rank+1 AS rank, votes
FROM (SELECT COUNT(candidate_id) AS votes
FROM candidate_votes
WHERE candidate_position = #position
GROUP BY candidate_id
ORDER BY votes DESC) vr
JOIN (select #rank := 0) r
GROUP BY rank
HAVING rank = #numwinners
) vt
)
ORDER BY v.votes DESC
Output for your data with #position=2 and #numwinners=3:
firstname lastname votes
doc7_fn doc7_ln 2
doc4_fn doc4_ln 1
doc1_fn doc1_ln 1
doc6_fn doc6_ln 1
doc5_fn doc5_ln 1
doc8_fn doc8_ln 1
doc9_fn doc9_ln 1
Output for your data with #position=1 and #numwinners=1:
firstname lastname votes
doc3_fn doc3_ln 2
doc2_fn doc2_ln 2
If you want to ensure the query works even if the value of #numwinners is greater than the number of candidates, change:
HAVING rank = #numwinners
to
HAVING rank = LEAST(#numwinners, (SELECT COUNT(DISTINCT candidate_id)
FROM candidate_votes
WHERE candidate_position = #position))
Demo

Categorywise bulk update: MySql

I have a table for article that save the category for each article.
I want to update the sort order, but categorywise using a single query.
I am using this for data migration using bulk inserts and updated, so it looping the data is not a feasible solution.
id category_id sort_order
10 5 1
11 5 2
12 5 3
13 6 1
14 6 2
15 6 3
16 7 1
17 8 1
18 8 2
19 7 2
20 8 3
Is it possible to do this in a single query?
I have written the query for update sequentially on all rows. But unable to do it categorywise. The query i have written is below
SET #so = 0;
UPDATE article A
INNER JOIN category M ON A.category_id = M.id
SET A.sort_id = (#i := #i + 1)
WHERE A.user_id = 1;
SQLFIDDLE
I'd use a temporary table with a composite primary key to autogenerate the incrementing sort_order field...
http://sqlfiddle.com/#!9/3d59fe/1
-- populate test data
CREATE TABLE cp (id INT PRIMARY KEY,
category_id INT,
sort_order INT);
INSERT INTO cp VALUES
(10, 5, 1),
(11, 5, 2),
(12, 5, 3),
(13, 6, 1),
(14, 6, 2),
(15, 6, 3),
(16, 7, 1),
(17, 8, 1),
(18, 8, 2),
(19, 7, 2),
(20, 8, 3);
-- create temp table and generate sort order
CREATE TABLE tmp (
id INT NOT NULL,
category_id INT NOT NULL,
sort_order INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (category_id,sort_order)
) ENGINE=MyISAM;
INSERT INTO tmp (id, category_id)
SELECT id, category_id FROM cp
ORDER BY rand();
-- update original table
UPDATE cp JOIN tmp USING (id)
SET cp.sort_order = tmp.sort_order;