MYSQL 5.6 get latest data of each user - mysql

My Database table is as shown below. I need to get latest mark of each student. Latest entry is the row with maximum udate and maximum oder. (The oder will be incremented by one on each entry with same date)
In my example, I have two students Mujeeb, Zakariya and two subjects ENGLISH, MATHS. I need to get latest mark of each student for each subject. My expectd result is as follows
My sample data is
DROP TABLE IF EXISTS `students`;
CREATE TABLE IF NOT EXISTS `students` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`udate` date NOT NULL,
`oder` int(11) NOT NULL,
`name` varchar(20) NOT NULL,
`Subject` varchar(20) NOT NULL,
`mark` int(11) NOT NULL,
PRIMARY KEY (`uid`)
) ENGINE=MyISAM AUTO_INCREMENT=13 DEFAULT CHARSET=latin1;
INSERT INTO `students` (`uid`, `udate`, `oder`, `name`, `Subject`, `mark`) VALUES
(1, '2021-08-01', 1, 'Mujeeb', 'ENGLISH', 10),
(2, '2021-08-01', 1, 'Zakariya', 'ENGLISH', 20),
(3, '2021-08-10', 2, 'Mujeeb', 'ENGLISH', 50),
(4, '2021-08-11', 2, 'Zakariya', 'ENGLISH', 60),
(5, '2021-08-02', 1, 'Mujeeb', 'ENGLISH', 100),
(6, '2021-08-03', 1, 'Zakariya', 'ENGLISH', 110),
(7, '2021-08-10', 1, 'Mujeeb', 'ENGLISH', 500),
(8, '2021-08-11', 1, 'Zakariya', 'ENGLISH', 600),
(9, '2021-08-01', 2, 'Mujeeb', 'MATHS', 100),
(10, '2021-08-01', 2, 'Zakariya', 'MATHS', 75),
(11, '2021-08-10', 3, 'Mujeeb', 'MATHS', 50),
(12, '2021-08-11', 3, 'Zakariya', 'MATHS', 60);

Use NOT EXISTS:
SELECT s1.*
FROM students s1
WHERE NOT EXISTS (
SELECT 1
FROM students s2
WHERE s2.name = s1.name AND s2.Subject = s1.Subject
AND (s2.udate > s1.udate OR (s2.udate = s1.udate AND s2.oder > s1.oder))
);
Or with a correlated subquery in the WHERE clause:
SELECT s1.*
FROM students s1
WHERE s1.uid = (
SELECT s2.uid
FROM students s2
WHERE s2.name = s1.name AND s2.Subject = s1.Subject
ORDER BY s2.udate DESC, s2.oder DESC LIMIT 1
);
See the demo.

As ROW_NUMBER() function doesn't work at lower version of MySQL, So alternate way of row_number() is used for this solution.
-- MySQL (v5.6)
SELECT p.uid, p.udate, p.oder, p.name, p.Subject, p.mark
FROM (SELECT #row_no := IF((#prev_val = t.name && #prev_val1 = t.Subject), #row_no + 1, 1) AS row_number
, #prev_val := t.name AS name
, #prev_val1 := t.Subject AS Subject
, t.mark
, t.oder
, t.uid
, t.udate
FROM students t,
(SELECT #row_no := 0) x,
(SELECT #prev_val := '') y,
(SELECT #prev_val1 := '') z
ORDER BY t.name, t.Subject, t.udate DESC, t.oder DESC ) p
WHERE p.row_number = 1
ORDER BY p.name, p.Subject;
Please check the url http://sqlfiddle.com/#!9/b5befe/18

Related

MySQL Attendance IN & OUT columns with correct times

I have a database for attendance, it works fine as long as the person does not work over 2 dates. I want to utilize IN and OUT system for records but I do not know how to do the final step, and what I saw on the forum does not work on MySQL or I am doing something wrong there.
This is my database and queries are under.
BTW Database is built using PHPmyadmin and MySQL Workbench.
CREATE TABLE `entries` (
`indexing` int(11) NOT NULL,
`emp_id` int(5) NOT NULL,
`Date` datetime DEFAULT current_timestamp() ) ;
INSERT INTO `entries` (`indexing`, `emp_id`, `Date`) VALUES
(61, 1, '2020-07-07 05:41:36'),
(62, 1, '2020-07-07 05:44:21'),
(63, 2, '2020-07-07 05:44:36'),
(64, 3, '2020-07-07 05:49:23'),
(65, 2, '2020-07-07 05:49:39'),
(66, 3, '2020-07-07 05:50:00'),
(67, 4, '2020-07-07 09:56:51'),
(68, 5, '2020-07-07 09:57:13'),
(69, 3, '2020-07-07 09:57:18'),
(70, 2, '2020-07-07 09:57:28'),
(71, 1, '2020-07-07 09:57:42'),
(72, 4, '2020-07-07 09:57:49'),
(73, 5, '2020-07-07 09:59:38'),
(74, 1, '2020-07-08 05:59:42'),
(75, 2, '2020-07-08 06:00:05'),
(76, 3, '2020-07-08 06:38:20'),
(77, 1, '2020-07-08 09:47:43'),
(78, 4, '2020-07-08 09:56:14'),
(79, 5, '2020-07-08 09:56:47'),
(80, 1, '2020-07-08 09:56:59'),
(81, 3, '2020-07-08 09:57:34'),
(82, 2, '2020-07-08 09:58:07'),
(83, 4, '2020-07-08 09:58:11'),
(84, 5, '2020-07-08 09:59:20'),
(85, 5, '2020-07-08 09:59:50'),
(86, 4, '2020-07-08 11:08:36'),
(87, 3, '2020-07-08 11:09:30');
CREATE TABLE `user` (
`emp_id` int(5) NOT NULL,
`Name` varchar(50) NOT NULL,
`company` set('First','second') NOT NULL DEFAULT 'First',
`department` set('Outbound','Inbound','UE','Returns','QC','Cleaner','Admin','IT
Technician','Supervisor','Manager') NOT NULL,
`driver` set('PPT','VNA','HLOP','CB','PPT VNA HLOP','PPT HLOP','PPT CB') DEFAULT NULL
) ;
INSERT INTO `user` (`emp_id`, `Name`, `company`, `department`, `driver`) VALUES
(1, 'Micinka', 'second', 'IT Technician', ''),
(2, 'Dusbica', 'First', 'IT Technician', ''),
(3, 'Klaudocka', 'First', 'Returns', ''),
(4, 'Patrycginis', 'First', 'Cleaner', ''),
(5, 'Stuistow', 'First', 'Cleaner', '');
--
ALTER TABLE `entries`
ADD PRIMARY KEY (`indexing`),
ADD KEY `emp_id` (`emp_id`);
--
-- Indexes for table `user`
--
ALTER TABLE `user`
ADD PRIMARY KEY (`emp_id`);
-- Constraints for table `entries`
--
ALTER TABLE `entries`
ADD CONSTRAINT `entries_ibfk_1` FOREIGN KEY (`emp_id`) REFERENCES `user` (`emp_id`) ON DELETE CASCADE;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=#OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=#OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=#OLD_COLLATION_CONNECTION */;
This are the Queries, and last one is how I would want the table look like but with IN and OUT times correct, now are both same.
select entries.emp_id, entries.Date, dense_rank() over (partition by entries.emp_id order by entries.indexing) % 2 AS 'IN and OUT' from entries;
drop table report_inout;
create view report_inout as select entries.emp_id, entries.Date,
CASE WHEN DENSE_RANK() OVER (PARTITION BY entries.emp_id ORDER BY entries.Date) % 2 = 0
THEN 'OUT' ELSE 'IN' END AS `IN and OUT`
FROM entries
ORDER BY
entries.indexing;
select date_format(report_inout.Date,'%d/%M/%Y') as `Date`,user.Name, time_format(report_inout.Date,'%H:%i:%s') as `IN`, time_format(report_inout.Date,'%H:%i:%s') as `OUT`,
user.company as Company,user.department as Department from report_inout
join user on user.emp_id = report_inout.emp_id
group by user.Name, report_inout.`In and Out`;
This are the results from my queries i posted.
emp_id;"Date";"IN and OUT"
1;"2020-07-07 05:41:36";"IN"
1;"2020-07-07 05:44:21";"OUT"
2;"2020-07-07 05:44:36";"IN"
3;"2020-07-07 05:49:23";"IN"
2;"2020-07-07 05:49:39";"OUT"
3;"2020-07-07 05:50:00";"OUT"
4;"2020-07-07 09:56:51";"IN"
5;"2020-07-07 09:57:13";"IN"
3;"2020-07-07 09:57:18";"IN"
2;"2020-07-07 09:57:28";"IN"
1;"2020-07-07 09:57:42";"IN"
4;"2020-07-07 09:57:49";"OUT"
5;"2020-07-07 09:59:38";"OUT"
1;"2020-07-08 05:59:42";"OUT"
2;"2020-07-08 06:00:05";"OUT"
3;"2020-07-08 06:38:20";"OUT"
1;"2020-07-08 09:47:43";"IN"
4;"2020-07-08 09:56:14";"IN"
5;"2020-07-08 09:56:47";"IN"
1;"2020-07-08 09:56:59";"OUT"
3;"2020-07-08 09:57:34";"IN"
2;"2020-07-08 09:58:07";"IN"
4;"2020-07-08 09:58:11";"OUT"
5;"2020-07-08 09:59:20";"OUT"
5;"2020-07-08 09:59:50";"IN"
and last query is this one, but it has always same time in IN and OUT
Date;"Name";"IN";"OUT";"Company";"Department"
08/July/2020;"Dusbica";"09:58:07";"09:58:07";"First";"IT Technician"
08/July/2020;"Dusbica";"06:00:05";"06:00:05";"First";"IT Technician"
08/July/2020;"Klaudocka";"09:57:34";"09:57:34";"First";"Returns"
08/July/2020;"Klaudocka";"11:09:30";"11:09:30";"First";"Returns"
08/July/2020;"Micinka";"09:47:43";"09:47:43";"second";"IT Technician"
08/July/2020;"Micinka";"09:56:59";"09:56:59";"second";"IT Technician"
08/July/2020;"Patrycginis";"11:08:36";"11:08:36";"First";"Cleaner"
08/July/2020;"Patrycginis";"09:58:11";"09:58:11";"First";"Cleaner"
08/July/2020;"Stuistow";"09:59:50";"09:59:50";"First";"Cleaner"
08/July/2020;"Stuistow";"09:59:20";"09:59:20";"First";"Cleaner"
Assuming that:
1st record for each separate emp_id is IN event
There is no lost events
WITH cte AS ( SELECT emp_id, `Date`,
ROW_NUMBER() OVER (PARTITION BY emp_id ORDER BY `Date`) - 1 rn
FROM entries )
SELECT t1.emp_id, user.name, t1.`Date` in_date, t2.`Date` out_date
FROM user
JOIN cte t1 ON user.emp_id = t1.emp_id
LEFT JOIN cte t2 ON t1.emp_id = t2.emp_id
AND t1.rn DIV 2 = t2.rn DIV 2
AND t2.rn MOD 2
WHERE NOT t1.rn MOD 2
ORDER BY emp_id, in_date;
fiddle
Idea.
We enumerate all rows for each employee separately starting with zero. So first IN is 0, first OUT is 1, 2nd IN is 2 and so on.
You can see that matched IN and OUT events will give the same result after integer divide their numbers by 2. And the reminder for IN will be 0 whereas for OUT it will be 1.
This is enough for correct joining.
Second copy of CTE table is joining using LEFT join because the last IN row may have no according OUT row - this means that the employee is now present at the object. And final row will contain NULL in out_date column in this case.

SQL Join multiple rows

I am looking for how to select in a JOIN only the line with the cheapest amount. Because the ON clause responds to multiple lines.
By default, MySQL takes the first line that it finds and I can not act on it.
SELECT g.id
, g.name
, p.description
, x.annually
FROM tblproductgroups g
JOIN tblproducts p
ON p.gid = g.id
AND p.hidden = 0
JOIN tblpricing x
ON x.relid = p.id
WHERE g.hidden = 0
AND g.id in (1,2,3)
AND x.type = 'product'
GROUP
BY g.id
I have to modify the JOIN of such tblpricing, but any comparison operation of the column "annually" gives me an error.
Edit: samples
CREATE TABLE `tblproductgroups` (
`id` int(10) NOT NULL,
`name` text COLLATE utf8_unicode_ci NOT NULL,
`hidden` tinyint(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `tblproductgroups` (`id`, `name`, `hidden`) VALUES
(1, 'Hébergement WEB', 0),
(2, 'Serveurs virtuels KVM', 0),
(3, 'Serveurs dédiés Pro', 0),
(5, 'Colocation', 0);
CREATE TABLE `tblproducts` (
`id` int(10) NOT NULL,
`gid` int(10) NOT NULL,
`name` text COLLATE utf8_unicode_ci NOT NULL,
`description` text COLLATE utf8_unicode_ci NOT NULL,
`hidden` tinyint(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `tblproducts` (`id`, `gid`, `name`, `description`, `hidden`) VALUES
(1, 1, 'Web Basic 2018', 'blablabla', 0),
(2, 1, 'Web Classic 2018', 'blablabla', 0),
(3, 1, 'Web Advanced 2018', 'blablabla', 0),
(5, 2, 'VPS Basic', 'blablabla', 0),
(6, 2, 'VPS Classic', 'blablabla', 0),
(7, 2, 'VPS Advanced', 'blablabla', 0),
(8, 3, 'SD-S 2018', 'blablabla', 0),
(9, 3, 'SD-L 2016', 'blablabla', 1),
(10, 3, 'SD-M 2018', 'blablabla', 0),
(11, 3, 'SD-XL 2018', 'blablabla', 0);
CREATE TABLE `tblpricing` (
`id` int(10) NOT NULL,
`type` enum('product','addon') COLLATE utf8_unicode_ci NOT NULL,
`relid` int(10) NOT NULL,
`annually` decimal(10,2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `tblpricing` (`id`, `type`, `relid`, `annually`) VALUES
(1, 'product', 1, '30'),
(39, 'product', 2, '20'),
(40, 'product', 3, '10'),
(42, 'product', 5, '100'),
(43, 'product', 6, '50'),
(44, 'product', 7, '25'),
(45, 'product', 8, '2000'),
(46, 'product', 9, '1000'),
(47, 'product', 9, '500'),
(48, 'product', 10, '250');
Result of my query is:
1 Hébergement WEB blablabla 30.00
2 Serveurs virtuels KVM blablabla 100.00
3 Serveurs dédiés Pro blablabla 2000.00
the correct result is:
1 Hébergement WEB blablabla 10.00
2 Serveurs virtuels KVM blablabla 25.00
3 Serveurs dédiés Pro blablabla 250.00
Crudely...
SELECT a.*
FROM
( SELECT g.id
, g.name
, p.description
, x.annually
FROM tblproductgroups g
JOIN tblproducts p
ON p.gid = g.id
AND p.hidden = 0
JOIN tblpricing x
ON x.relid = p.id
WHERE g.hidden = 0
AND g.id in (1,2,3)
AND x.type = 'product'
) a
JOIN
( SELECT id
, MIN(annually) annually
FROM
( SELECT g.id
, g.name
, p.description
, x.annually
FROM tblproductgroups g
JOIN tblproducts p
ON p.gid = g.id
AND p.hidden = 0
JOIN tblpricing x
ON x.relid = p.id
WHERE g.hidden = 0
AND g.id in (1,2,3)
AND x.type = 'product'
) x
GROUP
BY id
) b
ON b.id = a.id
AND b.annually = a.annually
This should do it :
SELECT g.id
, g.name
, p.name
, x.annually
FROM tblproductgroups g
JOIN tblproducts p
ON p.gid = g.id
AND p.hidden = 0
JOIN tblpricing x
ON x.relid = p.id
/* Subtable containing the minimum annual fee per group */
JOIN (SELECT subg.id, MIN(subx.annually) AS annually FROM tblproductgroups subg
INNER JOIN tblproducts subp On subg.id = subp.gid
AND subp.hidden = 0
INNER JOIN tblpricing subx ON subx.relid = subp.id
WHERE subg.hidden = 0
AND subg.id in (1,2,3)
AND subx.type = 'product'
GROUP BY subg.id) m
ON g.id = m.id AND x.annually = m.annually
WHERE g.hidden = 0
AND g.id in (1,2,3)
AND x.type = 'product'
Don't use GROUP BY if you're not actually using any aggregation function in your column definitions. It might work in MySQL but the results will be unpredictable unless you know exactly what you're doing.

Ponderate average MYSQL

We have a little simulator of a tour-operator DB (MYSQL) and we are asked to get a Query that gives us the weighted avg of duration of the tours that we have.
https://en.wikipedia.org/wiki/Weighted_arithmetic_mean
Using subquery I got to this point where I have the days that each tour lasts and the weight of each tour from the total of tours, but I am stuck and don't know how to get the weighted avg from here. I know I have to use another select from the result I already got but I would appreciate some help.
SQLfiddle down here:
http://sqlfiddle.com/#!9/53d80/2
Tables and data
CREATE TABLE STAGE
(
ID INT AUTO_INCREMENT NOT NULL,
TOUR INT NOT NULL,
TYPE INT NOT NULL,
CITY INT NOT NULL,
DAYS INT NOT NULL,
PRIMARY KEY (ID)
);
CREATE TABLE TOUR
(
ID INT AUTO_INCREMENT NOT NULL,
DESCRIPTION VARCHAR(255) CHARACTER SET UTF8 COLLATE UTF8_UNICODE_CI
NOT NULL,
STARTED_ON DATE NOT NULL,
TYPE INT NOT NULL,
PRIMARY KEY (ID)
);
INSERT INTO TOUR (DESCRIPTION, STARTED_ON, TYPE) VALUES
('Mediterranian Cruise','2018-01-01',3),
('Trip to Nepal','2017-12-01',1),
('Tour in Nova York','2015-04-24',5),
('A week at the Amazones','2014-09-11',2),
('Visiting the Machu Picchu','2013-02-19',4);
INSERT INTO STAGE (TOUR, TYPE, CITY, DAYS) VALUES
(1, 1, 38254, 1),
(1, 2, 22460, 3),
(1, 2, 47940, 3),
(1, 2, 42600, 4),
(1, 3, 38254, 1),
(2, 1, 13097, 1),
(2, 2, 29785, 5),
(2, 3, 13097, 1),
(3, 1, 788, 2); ,
(3, 2, 48019, 6),
(3, 3, 788, 1),
(4, 1, 38254, 2),
(4, 2, 8703, 3);,
(4, 3, 38254, 4),
(5, 1, 10453, 1),
(5, 2, 32045, 5),
(5, 3, 10453, 2);
Query:
SELECT
AVG(TD.TOUR_DAYS) AS AVERAGE_DAYS,
COUNT(TD.TOUR_ID) AS WEIGHT
FROM
(
SELECT
TOUR.ID AS TOUR_ID,
SUM(DAYS) AS TOUR_DAYS,
COUNT(STAGE.ID) AS STAGE_DAYS
FROM
TOUR
INNER JOIN
STAGE
ON
TOUR.ID = STAGE.TOUR
GROUP BY
TOUR.ID
) AS TD
GROUP BY
TD.TOUR_DAYS
weigthed avg would be:
(1×7+1×8+2×9+1×12) / (1+1+2+1) = 9
Wheighted AVG can be calculated with SUM(value * wheight) / SUM(wheight). In your case:
SELECT SUM(AVERAGE_DAYS * WEIGHT) / SUM(WEIGHT)
FROM (
SELECT
AVG(TD.TOUR_DAYS) AS AVERAGE_DAYS,
COUNT(TD.TOUR_ID) AS WEIGHT
FROM
(
SELECT
TOUR.ID AS TOUR_ID,
SUM(DAYS) AS TOUR_DAYS,
COUNT(STAGE.ID) AS STAGE_DAYS
FROM
TOUR
INNER JOIN
STAGE
ON
TOUR.ID = STAGE.TOUR
GROUP BY
TOUR.ID
) AS TD
GROUP BY
TD.TOUR_DAYS
) sub
http://sqlfiddle.com/#!9/53d80/4
I'm not 100% sure, but it looks like the following query is doing exactly the same:
SELECT AVG(TOUR_DAYS)
FROM (
SELECT TOUR, SUM(DAYS) AS TOUR_DAYS
FROM STAGE
GROUP BY TOUR
) sub;
Or even without any subqueries:
SELECT SUM(DAYS) / COUNT(DISTINCT TOUR)
FROM STAGE;
That would mean, the requirement should be simplified to "Get average number of days per tour".

sum of multiple products with show rows concerned using array

I have a table that has 3 products description (Product_1, Product_2, Product_3) and Each product of some reference AND quantity with location of every row
Iwant show all rows for product_1, product_2, product_3 which the sum of each product on Array variable as an example
$globalProduct = array(21000, 18000, 1000);
$sumProduct_1 = $globalProduct[0];
$sumProduct_2 = $globalProduct[1];
$sumProduct_3 = $globalProduct[2];
Ihave query mysql for one product but I found it difficult to use 3 products.
this query for one product, it's working correctly
SET #runtot=0;
SELECT p_reference, p_id, p_description, p_quantity, p_location, (#runtot := #runtot + p_quantity) AS runningTotal
FROM (
SELECT p_reference, p_id, p_description, p_quantity, p_location
FROM product_table
WHERE p_description = 'product_1'
ORDER BY p_reference, p_quantity
) AS l
WHERE #runtot <= 21000;
this follow source code for create Product_table
CREATE TABLE IF NOT EXISTS `product_table` (
`p_id` int(11) NOT NULL,
`p_description` varchar(50) NOT NULL,
`p_reference` varchar(25) NOT NULL,
`p_location` varchar(25) NOT NULL,
`p_quantity` float(11,3) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=18 ;
--
-- Contenu de la table `product_table`
--
INSERT INTO `product_table` (`p_id`, `p_description`, `p_reference`, `p_location`, `p_quantity`) VALUES
(1, 'Product_1', '1A00001', 'QD01', 7000.000),
(2, 'Product_1', '1A00001', 'QD02', 7000.000),
(3, 'Product_1', '1A00007', 'QD03', 7000.000),
(4, 'Product_1', '1A00008', 'QD04', 7000.000),
(5, 'Product_2', '1A00002', 'AB01', 6500.000),
(6, 'Product_2', '1A00004', 'AB02', 6500.000),
(7, 'Product_2', '1A00005', 'AB03', 6500.000),
(8, 'Product_2', '1A00003', 'DB01', 6500.000),
(9, 'Product_2', '1A00009', 'DB02', 6500.000),
(10, 'Product_2', '1A00002', 'DB03', 6500.000),
(11, 'Product_2', '1A00002', 'DB04', 6500.000),
(12, 'Product_3', '1A00010', 'FD01', 5000.000),
(13, 'Product_3', '1A00015', 'DS02', 5000.000),
(14, 'Product_3', '1A00017', 'DS03', 5000.000),
(15, 'Product_3', '1A00018', 'DS04', 5000.000);
Perhaps it is as easy as this?
SELECT
p_description,
sum(p_quantity) AS runningTotal
FROM
product_table
GROUP BY
p_description
Ok, a bit more complicated then:
SELECT * FROM
(
SELECT
a.*, c.p_description as nextProduct,
(#gc := #gc + a.p_quantity) as uu, if(a.p_description != c.p_description, #gc := 0, 1) as `reset`
FROM
(SELECT #gc := 0) as b, product_table as a
LEFT OUTER JOIN product_table as c ON a.p_id = c.p_id -1
) as a
WHERE uu <= 21000

Two Limit Clauses in one statement?

I am trying to build a product review page where users can comment any submitted reviews like Amazon. I want the page to display 3 user reviews as well as 2 responses to each of the reviews if they exist.
Here's the table
CREATE TABLE product_review
(`ID` int, `username` varchar(21), `review_title` varchar(30), `review_or_reply` int)
;
INSERT INTO product_review
(`ID`, `username`, `review_title`, `review_or_reply`)
VALUES
(1, 'Tom', 'Rip-off', 0),
(2, 'Peter', 'Rip-off', 1),
(3, 'May', 'Rip-off', 1),
(4, 'June', 'Rip-off', 1),
(5, 'Tommy', 'Worth the Price', 0),
(6, 'Sammy', 'Worth the Price', 1),
(7, 'Sam', 'Worth the Price',1),
(8, 'Bryan','Worth the Price',1),
(9, 'Sally', 'Average Product', 0)
;
The review_or_reply field is effectively a Yes or No field, where 0 means it's a review and 1 is the review's comments by other users.
Is there a single select statement that can limit 3 reviews and bring up two of their comments? For example:
Select `username`,`review_title`,`reply` from product_review where review_or_reply ='0' Limit 3
Select `username`,`review_title`,`reply` from product_review where review_or_reply = '1' and title = 'Rip-off' Limit 2
Select `username`,`review_title`,`reply` from product_review where review_or_reply = '1' and title = 'Worth the Price' Limit 2
Select `username`,`review_title`,`reply` from product_review where review_or_reply = '1' and title = 'Average Product' Limit 2
I want the output to be like this:
username review_title review_or_reply
Tom Rip-off 0
Peter Rip-off 1
May Rip-off 1
Tommy Worth the Price 0
Sammy Worth the Price 1
Sam Worth the Price 1
Sally Average Product 0
this will return 3 review_titles and then pull out two responses to that
SELECT
pr.*,
IF( #A = t.review_title,
IF(#B = 3, #B := 1, #B := #B +1)
, #B
) AS group_col,
#A := t.review_title
FROM (
SELECT
id,
username,
review_title
FROM product_review
WHERE reply ='0' LIMIT 3
) t
JOIN product_review pr ON pr.review_title=t.review_title
CROSS JOIN (SELECT #A := "", #B := 1) AS temp
GROUP BY group_col, review_title
ORDER BY id;
EDIT:
if there are more than one reply that is 0 in the database like so then this query will check for that. (since you did specify in the other queries that the reply had to be 1).
INSERT INTO product_review
(`ID`, `username`, `review_title`, `reply`)
VALUES
(1, 'Tom', 'Rip-off', 0),
(2, 'Peter', 'Rip-off', 1),
(3, 'May', 'Rip-off', 0),
(4, 'June', 'Rip-off', 1),
(5, 'Tommy', 'Worth the Price', 0),
(6, 'Sammy', 'Worth the Price', 1),
(7, 'Sam', 'Worth the Price',1),
(8, 'Bryan','Worth the Price',1),
(9, 'Sally', 'Average Product', 0),
(10, 'Timothy', 'Rip-off', 1)
notice that at id 3 there is a reply of 0 with id 10 a reply of 1. this query will correctly skip the reply = 0.
SELECT
pr.*,
IF( #A = t.review_title,
IF(pr.reply = 0, 1,
IF(#B = 3, #B := 1, #B := #B +1)
), #B
) AS group_col,
#A := t.review_title
FROM (
SELECT
DISTINCT
id,
username,
review_title
FROM product_review
WHERE reply ='0'
GROUP BY review_title
LIMIT 3
) t
JOIN product_review pr ON pr.review_title=t.review_title
CROSS JOIN (SELECT #A := "", #B := 1) AS temp
GROUP BY group_col, review_title
ORDER BY id;
DEMO
...or slower but simpler...
SELECT x.*
FROM product_review x
JOIN product_review y
ON y.review_title = x.review_title
AND y.id <= x.id
GROUP
BY x.id
HAVING COUNT(*) <= 3
ORDER
BY MIN(y.id)
LIMIT 3;