getting most recent result - mysql, left join - mysql

I have 3 tables:
'art' contains a list of products ('art_id', 'art_nom' for name)
'mag' is the list of shops ('mag_id')
'pri' contains the prices of the products for a specific shop. There are several prices for one product, and even several prices for one shop, depending on the date ('pri_pri' is the price, 'pri_dat' is the date at which the price was registered and is on the datetime format).
I want the list of the products with 3 associated most recent prices (for the 3 shops selected). I got them with this request:
SELECT art.art_id, art_nom,
pri0.pri_pri AS pri_pri0,
pri1.pri_pri AS pri_pri1,
pri2.pri_pri AS pri_pri2
FROM (((art
LEFT JOIN (SELECT pri_pri, art_id, mag_id AS nbr_pri0 FROM pri WHERE mag_id = '7081' GROUP BY art_id) AS pri0 ON art.art_id = pri0.art_id)
LEFT JOIN (SELECT pri_pri, art_id, mag_id AS nbr_pri1 FROM pri WHERE mag_id = '14432' GROUP BY art_id) AS pri1 ON art.art_id = pri1.art_id)
LEFT JOIN (SELECT pri_pri, art_id, mag_id AS nbr_pri2 FROM pri WHERE mag_id = '14515' GROUP BY art_id) AS pri2 ON art.art_id = pri2.art_id)
I receive the list as I want, except that I don't have the most recent price.
So if somebody could help me adding a condition getting the most recent price, that would be really appreciated.
And I am really not sure if this way is very efficient. So if you have some advice on simplifying it...
Thanks !
note: i simplified my request so it is easier to read. If you want the complete request, with 5 prices and an 'ORDER BY' on the quantity of prices, here it is:
SELECT art.art_id, cat_id1, cat_id2, cat_id3, art_ean, art_nom, art_unt, art_qtt,
mrq_nom, mrq_img,
(IFNULL(nbr_pri0, 0) + IFNULL(nbr_pri1, 0) + IFNULL(nbr_pri2, 0) + IFNULL(nbr_pri3, 0) + IFNULL(nbr_pri4, 0)) AS nbr_pri,
nbr_pri0, pri0.pri_pri AS pri_pri0,
nbr_pri1, pri1.pri_pri AS pri_pri1,
nbr_pri2, pri2.pri_pri AS pri_pri2,
nbr_pri3, pri3.pri_pri AS pri_pri3,
nbr_pri4, pri4.pri_pri AS pri_pri4
FROM mrq, (((((art
LEFT JOIN (SELECT pri_pri, art_id, mag_id, COUNT(pri_id) AS nbr_pri0 FROM pri WHERE mag_id = '7081' GROUP BY art_id) AS pri0 ON art.art_id = pri0.art_id)
LEFT JOIN (SELECT pri_pri, art_id, mag_id, COUNT(pri_id) AS nbr_pri1 FROM pri WHERE mag_id = '14432' GROUP BY art_id) AS pri1 ON art.art_id = pri1.art_id)
LEFT JOIN (SELECT pri_pri, art_id, mag_id, COUNT(pri_id) AS nbr_pri2 FROM pri WHERE mag_id = '14515' GROUP BY art_id) AS pri2 ON art.art_id = pri2.art_id)
LEFT JOIN (SELECT pri_pri, art_id, mag_id, COUNT(pri_id) AS nbr_pri3 FROM pri WHERE mag_id = '12458' GROUP BY art_id) AS pri3 ON art.art_id = pri3.art_id)
LEFT JOIN (SELECT pri_pri, art_id, mag_id, COUNT(pri_id) AS nbr_pri4 FROM pri WHERE mag_id = '8136' GROUP BY art_id) AS pri4 ON art.art_id = pri4.art_id)
WHERE mrq.mrq_id = art.mrq_id
ORDER BY nbr_pri DESC
LIMIT 0,50
edit: here are the scripts for the tables as asked by Nitu and Strawberry (i hope so, i am not sure of what "DDLs" and "sqlfiddle" mean):
CREATE TABLE IF NOT EXISTS `art` (
`art_id` int(11) NOT NULL AUTO_INCREMENT,
`art_nom` char(40) NOT NULL,
PRIMARY KEY (`art_id`)
);
INSERT INTO `art` (`art_id`, `art_nom`) VALUES
(1, 'Coca-Cola classic'),
(2, 'Coca-Cola vanille'),
(3, 'Coca-Cola Cherry'),
(4, 'Coca-Cola Light lemon');
CREATE TABLE IF NOT EXISTS `mag` (
`mag_id` int(6) NOT NULL AUTO_INCREMENT,
`mag_vil` char(100) NOT NULL,
`mag_nom` char(100) NOT NULL,
PRIMARY KEY (`mag_id`)
);
INSERT INTO `mag` (`mag_id`, `mag_vil`, `mag_nom`) VALUES
(2, '01100 Oyonnax', 'Petit Casino Oyonnax'),
(3, '75001 Paris', 'Petit Casino Paris'),
(4, '69001 Lyon', 'Petit Casino Lyon');
CREATE TABLE IF NOT EXISTS `pri` (
`pri_id` int(11) NOT NULL AUTO_INCREMENT,
`art_id` int(11) NOT NULL,
`mag_id` int(6) NOT NULL,
`pri_pri` float NOT NULL,
`pri_dat` datetime NOT NULL,
PRIMARY KEY (`pri_id`)
);
INSERT INTO `pri` (`pri_id`, `art_id`, `mag_id`, `pri_pri`, `pri_dat`) VALUES
(1, 1, 14515, 2.61, '2014-08-12 17:28:48'),
(2, 1, 12458, 1.74, '2014-04-01 17:52:00'),
(3, 1, 12458, 2.39, '2014-03-15 17:52:00'),
(4, 3, 12458, 1.93, '2014-07-31 20:00:00'),
(5, 3, 8136, 1.21, '2014-08-08 14:02:00');
edit of the edit: for this data, I want to receive the price 1.74 (pri_id = 2) for the product art_id = 1, because the date is more recent than for the price 2.39 (pri_id = 3)

You can change your subqueries to something like
SELECT pri_date, MAX(pri_date) AS maxdate, pri_pri, art_id, mag_id, COUNT(pri_id) AS nbr_pri0
FROM pri
WHERE mag_id = '7081'
GROUP BY art_id
HAVING maxdate=pri_date
Which will give you the recent date.

Related

Find DISTINCT LAST record with SQL LEFT JOIN

I'm running MySQL 5.6.
I have two related tables:
CREATE TABLE Cars (
id INT NOT NULL AUTO_INCREMENT,
plate VARCHAR(16) NOT NULL,
flag TINYINT,
PRIMARY KEY(id)
)
and:
CREATE TABLE Rents (
id INT NOT NULL AUTO_INCREMENT,
out_date DATE NOT NULL,
in_date DATE,
car_id INT,
FOREIGN KEY (car_id) REFERENCES Cars(id),
PRIMARY KEY(id)
)
I can have multiple rents for each car (0 to many).
I need to select all vehicles in table Cars (with flag = 1) along with their status i.e. I need to know if each car is currently unavailable (only out_date is filled) or availabe (out_date and in_date filled) of course also vehicles without any rents are to be considered available.
The result set need to include out_date and in_date values [Update 17/07/2022].
I tought to use something like:
SELECT
*,
IF(Rents.in_date IS NOT NULL AND Rents.out_date IS NOT NULL, 1, IF(Rents.id IS NULL, 1, 0)) AS status
FROM Cars
LEFT JOIN Rents ON Cars.id = Rent.Car_id WHERE Cars.Flag = 1
but this of course will just return all the rows with positive flag match and a status evaluation (0 unavailable, 1 available):
id | plate | flag | id | out_date | in_date | car_id | status
---------------------------------------------------------------------
'1', 'FA787MX', '1', '1', '2022-07-14', '2022-07-15', '1', '1'
'1', 'FA787MX', '1', '2', '2022-07-16', NULL, '1', '0'
'3', 'AB124DF', '1', '4', '2022-07-13', '2022-07-14', '3', '1'
'4', 'CC666VC', '1', NULL, NULL, NULL, NULL, '1'
'5', 'GG435ED', '1', '5', '2022-07-16', NULL, '5', '0'
While I need to have this (edited 17/07/2022):
'1', 'FA787MX', '1', '2', '2022-07-16', NULL, '1', '0'
'3', 'AB124DF', '1', '4', '2022-07-13', '2022-07-14', '3', '1'
'4', 'CC666VC', '1', NULL, NULL, NULL, NULL, '1'
'5', 'GG435ED', '1', '5', '2022-07-16', NULL, '5', '0'
i.e. only the second row of FA787MX car should be mantained since it's the most recent out_date value (no matter if it's id is higher or lower).
For the sake of completeness: There is no guarantee that rental ids will be kept consistent with their rental history. In other words you cannot be sure that for a given car the rental where in_date = NULL is the correct one but you should compare them by out_date value.
Data sample:
INSERT INTO `Cars` (`id`, `plate`, `flag`) VALUES (1, 'FA787MX', 1);
INSERT INTO `Cars` (`id`, `plate`, `flag`) VALUES (2, 'EX431YY', 0);
INSERT INTO `Cars` (`id`, `plate`, `flag`) VALUES (3, 'AB124DF', 1);
INSERT INTO `Cars` (`id`, `plate`, `flag`) VALUES (4, 'CC666VC', 1);
INSERT INTO `Cars` (`id`, `plate`, `flag`) VALUES (5, 'GG435ED', 1);
INSERT INTO `Rents` (`id`, `out_date`, `in_date`, `car_id`) VALUES (1, '2022-07-14', '2022-07-15', 1);
INSERT INTO `Rents` (`id`, `out_date`, `in_date`, `car_id`) VALUES (2, '2022-07-16', NULL, 1);
INSERT INTO `Rents` (`id`, `out_date`, `in_date`, `car_id`) VALUES (3, '2022-07-16', NULL, 2);
INSERT INTO `Rents` (`id`, `out_date`, `in_date`, `car_id`) VALUES (4, '2022-07-13', '2022-07-14', 3);
INSERT INTO `Rents` (`id`, `out_date`, `in_date`, `car_id`) VALUES (5, '2022-07-16', NULL, 5);
One option is to join to find only those rentals that are still outstanding (in_date IS NULL). That will drop the old rentals having in_date not null.
Based on the updated requirements, there are a few ways to do it. One is a simple outer join to find the most recent rental per car to obtain the corresponding in_date as well...
MySQL 5.6 fiddle
SELECT Cars.*
, Rents.out_date
, Rents.in_date
, Rents.id IS NULL OR Rents.in_date IS NOT NULL AS status_final
FROM Cars
LEFT JOIN Rents
ON Cars.id = Rents.Car_id
LEFT JOIN Rents AS r2
ON Rents.out_date < r2.out_date
AND Rents.Car_id = r2.Car_id
WHERE Cars.Flag = 1
AND r2.Car_id IS NULL
ORDER BY Cars.id
;
The result:
id
plate
flag
out_date
in_date
status_final
1
FA787MX
1
2022-07-16
0
3
AB124DF
1
2022-07-13
2022-07-14
1
4
CC666VC
1
1
5
GG435ED
1
2022-07-16
0
Based on the original requirements: Try this (fiddle):
SELECT Cars.*
, Rents.in_date
, CASE WHEN in_date IS NOT NULL OR Rents.id IS NULL THEN 1 ELSE 0 END AS status_final
FROM Cars
LEFT JOIN Rents
ON Cars.id = Rents.Car_id
AND in_date IS NULL
WHERE Cars.Flag = 1
;
and if the results contain only those with in_date IS NULL, this reduces to:
SELECT Cars.*
, out_date
, Rents.in_date
, Rents.id IS NULL AS status_final
FROM Cars
LEFT JOIN Rents
ON Cars.id = Rents.Car_id
AND in_date IS NULL
WHERE Cars.Flag = 1
;
Result:
id
plate
flag
out_date
in_date
status_final
1
FA787MX
1
2022-07-16
0
3
AB124DF
1
1
4
CC666VC
1
1
5
GG435ED
1
2022-07-16
0
If your version of MySql is 8.0+ use ROW_NUMBER() window function to pick the latest row for each car in Rents:
SELECT c.*, r.*,
r.out_date IS NULL OR r.in_date IS NOT NULL status
FROM Cars c
LEFT JOIN (
SELECT *, ROW_NUMBER() OVER (PARTITION BY car_id ORDER BY out_date DESC) rn
FROM Rents
) r ON r.car_id = c.id AND r.rn = 1
WHERE c.flag = 1;
For previous versions use NOT EXISTS:
SELECT c.*, r.*,
r.out_date IS NULL OR r.in_date IS NOT NULL status
FROM Cars c
LEFT JOIN (
SELECT r1.*
FROM Rents r1
WHERE NOT EXISTS (
SELECT *
FROM Rents r2
WHERE r2.car_id = r1.car_id AND r2.out_date > r1.out_date
)
) r ON r.car_id = c.id
WHERE c.flag = 1;
See the demo.
If you imagine the result of your query as a table, you can easily write a query that would give you what you need (the subquery is just yours with the select spelled out to give a unique column name to the second id column, as it seemed useful - the only way to uniquely identify a row):
SELECT MAX(rent_id) FROM (
SELECT
Cars.id as id,
plate,
flag,
Rents.id as rent_id,
out_date,
in_date,
car_id,
IF(Rents.in_date IS NOT NULL AND Rents.out_date IS NOT NULL, 1, IF(Rents.id IS NULL, 1, 0)) AS status
FROM Cars
LEFT JOIN Rents ON Cars.id = Rents.car_id WHERE Cars.Flag = 1
) as rental_status
WHERE status = 0
GROUP BY car_id;
Which tells you which rows are interesting:
+--------------+
| MAX(rent_id) |
+--------------+
| 2 |
| 5 |
+--------------+
Now you can use a join to return the results of your initial query only for the interesting rows. To avoid having to spell out that query all over again, MySQL 8 has a way to stash the results of your core query and use it like a table:
WITH
status_data AS (
SELECT
Cars.id as id,
plate,
flag,
Rents.id as rent_id,
out_date,
in_date,
car_id,
IF(Rents.in_date IS NOT NULL AND Rents.out_date IS NOT NULL, 1, IF(Rents.id IS NULL, 1, 0)) AS status
FROM Cars
LEFT JOIN Rents ON Cars.id = Rents.car_id WHERE Cars.Flag = 1
)
SELECT * from status_data
JOIN (
SELECT MAX(rent_id) as rent_id FROM status_data
WHERE status = 0
GROUP BY car_id
) as ids using(rent_id);
Giving the result:
+---------+----+---------+------+------------+---------+--------+--------+
| rent_id | id | plate | flag | out_date | in_date | car_id | status |
+---------+----+---------+------+------------+---------+--------+--------+
| 2 | 1 | FA787MX | 1 | 2022-07-16 | NULL | 1 | 0 |
| 5 | 5 | GG435ED | 1 | 2022-07-16 | NULL | 5 | 0 |
+---------+----+---------+------+------------+---------+--------+--------+

Joining a single row to a query

I need to get a value from another table but where there may be 5/6 results, I only need to show the latest one. I've tried the following:
SELECT s.Mileage
, s.PurchasePrice
, v.make
, v.model
, v.vrm
, c.CleanLive
FROM StockBook s
LEFT
JOIN Vehicles v
ON v.VehicleID = s.VehicleID
LEFT
JOIN CapVals c
ON c.LeadID = (SELECT C1.CleanLive
FROM CapVals C1
WHERE s.LeadID = c.LeadID
ORDER
BY C1.Date
LIMIT 1
)
ORDER
BY StockBookID
Which is working as a query but not showing CleanLive value.
I've set up a sample data set and DB Fiddle here:
CREATE TABLE `Vehicles` (
`VehicleID` int(11) NOT NULL,
`vrm` varchar(15) NOT NULL,
`make` varchar(40) NOT NULL,
`model` varchar(40) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `StockBook` (
`StockBookID` int(11) NOT NULL,
`VehicleID` int(11) NOT NULL,
`LeadID` int(11) NOT NULL,
`Mileage` int(11) NOT NULL,
`PurchasePrice` decimal(15,2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `CapVals` (
`CapValsID` int(11) NOT NULL,
`LeadID` int(11) DEFAULT NULL,
`CleanLive` int(11) DEFAULT NULL,
`Date` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `Vehicles` (`VehicleID`, `vrm`, `make`, `model`) VALUES
(1, 'M900WRD', 'Vauxhall', 'Signum');
INSERT INTO `StockBook` (`StockBookID`, `LeadID`, `VehicleID`, `Mileage`, `PurchasePrice`) VALUES
(1, 1, 1, 17000, 15000.00);
INSERT INTO `CapVals` (`CapValsID`, `LeadID`, `CleanLive`, `Date`) VALUES
(6455, 1, 1540, '2019-12-04 15:02:29'),
(6456, 1, 1540, '2019-12-04 15:02:29'),
(6457, 1, 1540, '2019-12-04 15:02:29');
https://www.db-fiddle.com/f/b4fQuMVpXHGxqgYJ4ia92w/4
You can try this
SELECT Stock.Mileage, Stock.PurchasePrice, Vehi.make, Vehi.model, Vehi.vrm,
(SELECT CleanLive from CapVals a WHERE a.LeadID = Stock.LeadID ORDER BY DATE DESC LIMIT 1) AS CleanLive
FROM StockBook Stock
LEFT JOIN Vehicles Vehi
ON Stock.VehicleID=Vehi.VehicleID
ORDER BY StockBookID
SELECT v.vehicleID
, v.vrm
, v.make
, v.model
, s.stockbookid
, s.leadid
, s.mileage
, s.purchaseprice
, c.capvalsid
, c.cleanlive
, c.date
FROM vehicles v
JOIN stockbook s
ON s.vehicleid = v.vehicleid
JOIN capvals c
ON c.leadid = s.leadid
JOIN
( SELECT leadid,MAX(capvalsid) capvalsid FROM capvals GROUP BY leadid ) x
ON x.leadid = c.leadid
AND x.capvalsid = c.capvalsid;
+-----------+---------+----------+--------+-------------+--------+---------+---------------+-----------+-----------+---------------------+
| vehicleID | vrm | make | model | stockbookid | leadid | mileage | purchaseprice | capvalsid | cleanlive | date |
+-----------+---------+----------+--------+-------------+--------+---------+---------------+-----------+-----------+---------------------+
| 1 | M900WRD | Vauxhall | Signum | 1 | 1 | 17000 | 15000.00 | 6457 | 1540 | 2019-12-04 15:02:29 |
+-----------+---------+----------+--------+-------------+--------+---------+---------------+-----------+-----------+---------------------+
Use Row_number concept, which will avoid the duplicate rows and give you recent one,
FIDDLE DEMO
SELECT Stock.Mileage, Stock.PurchasePrice, Vehi.make, Vehi.model, Vehi.vrm,
X.CleanLive as CleanLive
FROM StockBook Stock
LEFT JOIN Vehicles Vehi ON Stock.VehicleID=Vehi.VehicleID
LEFT JOIN (SELECT #LeadID:=LeadID,C1.LeadID, CleanLive, C1.Date, #row_number:=CASE WHEN #LeadID = LeadID THEN #row_number + 1 ELSE 1 END AS num
FROM CapVals AS C1, (SELECT #LeadID:=0,#row_number:=0) as t ORDER BY C1.Date DESC) X ON X.LeadID = Stock.LeadID AND X.num = 1
ORDER BY StockBookID

filter all two tables to get all the data

I created a database for survey software. The two tables of the database are what I want to do, I want to get the average scores from the two date ranges and from a place, and get the ones without the answer as null or 0. I tried
SELECT
AVG(tbAnswers.averageScore)
FROM
tbDrivers
LEFT JOIN tbAnswers ON tbDrivers.driverId = tbAnswers.driverId
WHERE
tbDrivers.place = 'WDC'
GROUP BY
tbDrivers.driverId
But when I specify the date range, is not get the data of the drivers without answer.
SELECT AVG(tbAnswers.averageScore)
FROM tbDrivers LEFT JOIN tbAnswers ON tbDrivers.driverId = tbAnswers.driverId
WHERE tbDrivers.place = 'WDC'
AND answerDate BETWEEN '2018-11-28' AND '2018-12-03'
GROUP BY tbDrivers.driverId
Table structures:
CREATE TABLE `tbAnswers` (
`answerId` int(11) NOT NULL,
`answerDate` date NOT NULL,
`driverId` int(11) NOT NULL,
`score1` int(11) NOT NULL,
`score2` int(11) NOT NULL,
`score3` int(11) NOT NULL,
`averageScore` float NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `tbAnswers` (`answerId`, `answerDate`, `driverId`, `score1`, `score2`, `score3`, `averageScore`) VALUES
(10, '2018-11-28', 1032, 0, 0, 0, 0),
(11, '2018-11-29', 1032, 9, 8, 3, 6.67),
(12, '2018-11-30', 1032, 0, 3, 2, 1.67),
(13, '2018-11-30', 1035, 10, 2, 10, 7.34),
(14, '2018-11-01', 1032, 5, 5, 5, 5),
(15, '2018-12-03', 1035, 5, 5, 7, 5.67);
CREATE TABLE `tbDrivers` (
`driverId` int(11) NOT NULL,
`nameSurname` varchar(32) NOT NULL,
`place` varchar(64) NOT NULL,
`plate` varchar(8) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `tbDrivers` (`driverId`, `nameSurname`, `place`, `plate`) VALUES
(1032, 'Nick Oliver', 'WDC', 'B16186D'),
(1033, 'Nicholas Keller', 'WDC', 'ACG8095'),
(1034, 'Felipe Mendez', 'WDC', 'C26106E'),
(1035, 'Lowell Butler', 'WDC', '5123QK');
How can I solve this problem?
The problem arises because you have no records for driverid in tbanswers table.
Either make an entry in tbanswers or Use Query given by Forpas above or use this query
SELECT tbdrivers.driverid,
Avg(tbanswers.averagescore)
FROM tbdrivers
LEFT JOIN tbanswers
ON tbdrivers.driverid = tbanswers.driverid
WHERE tbdrivers.place = 'WDC'
AND answerdate BETWEEN '2018-11-28' AND '2018-12-03'
OR answerdate IS NULL
GROUP BY tbdrivers.driverid
Use your query which fetches the drivers that have at least 1 answer, UNION the drivers that have no answer:
(SELECT tbDrivers.driverId, AVG(tbAnswers.averageScore) AS avgscore
FROM tbDrivers LEFT JOIN tbAnswers ON tbDrivers.driverId = tbAnswers.driverId
WHERE tbDrivers.place = 'WDC'
AND answerDate BETWEEN '2018-11-28' AND '2018-12-03'
GROUP BY tbDrivers.driverId )
UNION
(SELECT t.driverId, NULL AS avgscore
FROM tbDrivers t
WHERE
NOT EXISTS (SELECT 1 FROM tbAnswers WHERE tbAnswers.driverId = t.driverId))
ORDER BY driverId
the result is:
driverId avgscore
1032 2.7800000111262
1033 (null)
1034 (null)
1035 6.505000114440918

mysql | Facet search advanced

I have tables:
CREATE TABLE IF NOT EXISTS `category` (
`id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `category` (`id`, `name`) VALUES
(1, 'Computers'),
(2, 'Bikes');
CREATE TABLE IF NOT EXISTS `fields` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`field_name` varchar(255) DEFAULT NULL,
`cid` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
INSERT INTO `fields` (`id`, `field_name`, `cid`) VALUES
(1, 'Processor', '1'),
(2, 'Display', '1'),
(3, 'Brand', '2');
CREATE TABLE IF NOT EXISTS `fields_values` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`field_id` int(11) DEFAULT NULL,
`field_value` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;
INSERT INTO `fields_values` (`id`, `field_id`, `field_value`) VALUES
(1, 1, 'Intel Pentium 3'),
(2, 2, '27 inch'),
(3, 3, 'BMX'),
(4, 1, 'AMD Radeon'),
(5, 1, 'Intel Atom'),
(6, 2, '22 inch');
CREATE TABLE IF NOT EXISTS `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`cid` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ;
INSERT INTO `products` (`id`, `name`, `cid`) VALUES
(1, 'Computer1', 1),
(2, 'Bike1.BMX', 2),
(3, 'Bike3', 2),
(4, 'Intel Atom', 1),
(5, 'Computer Radeon', 1);
CREATE TABLE IF NOT EXISTS `products_to_fields_values` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) DEFAULT NULL,
`field_value_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
INSERT INTO `products_to_fields_values` (`id`, `product_id`, `field_value_id`) VALUES
(1, 1, 1),
(2, 2, 3),
(3, 1, 2),
(4, 4, 5);
My request looks like:
SELECT ft.id field_id, ft.field_name, fvt.field_value, fvt.id field_value_id, COUNT( DISTINCT pid ) count
FROM FIELDS ft
JOIN fields_values fvt ON ( ft.id = fvt.field_id )
JOIN products_to_fields_values pfv ON ( pfv.field_value_id = fvt.id )
JOIN products pt ON ( pt.id = pfv.product_id )
LEFT JOIN (
SELECT ft.id field_id, ft.field_name, fvt.field_value, fvt.id field_value_id, pt.name, pt.id pid
FROM FIELDS ft
JOIN fields_values fvt ON ( ft.id = fvt.field_id )
JOIN products_to_fields_values pfv ON ( pfv.field_value_id = fvt.id )
JOIN products pt ON ( pt.id = pfv.product_id )
GROUP BY pt.id
)LJ ON pfv.product_id = LJ.pid
WHERE FIND_IN_SET( 1, pt.cid )
GROUP BY ft.field_name, fvt.field_value
LIMIT 0 , 30
This request will return (I'm trying to build faceted filter):
field_id field_name field_value field_value_id count
2 Display 27 inch 2 1
1 Processor Intel Atom 5 1
1 Processor Intel Pentium 3 1
But I have other values in this table: fields_values like: AMD Radeon and 22 inch.
Where is my mistake in the request?
Thanks!
EDIT:
I'm expect to getting result:
field_id field_name field_value field_value_id count
2 Display 22 inch 6 0
2 Display 27 inch 2 1
1 Processor AMD Radeon 4 0
1 Processor Intel Atom 5 1
1 Processor IntelPentium3 1 1
Where count is a products count.
Here is a work SQL, but im not sure you have build your structure correctly.
SELECT
ft.id field_id, ft.field_name, fvt.field_value, fvt.id as field_value_id, COUNT( DISTINCT pid ) count
FROM products AS pt
JOIN products_to_fields_values AS pfv ON pfv.product_id = pt.id
JOIN fields_values AS fvt ON fvt.field_id = pfv.field_value_id
JOIN fields AS ft on ft.id = fvt.field_id
LEFT JOIN (
SELECT ft.id field_id, ft.field_name, fvt.field_value, fvt.id field_value_id, pt.name, pt.id pid
FROM fields ft
JOIN fields_values fvt ON ( ft.id = fvt.field_id )
JOIN products_to_fields_values pfv ON ( pfv.field_value_id = fvt.id )
JOIN products pt ON ( pt.id = pfv.product_id )
GROUP BY pt.id
)LJ ON pfv.product_id = LJ.pid
WHERE FIND_IN_SET( 1, pt.cid )
GROUP BY ft.field_name, fvt.field_value
LIMIT 0 , 30
In short - u have error in this line:
JOIN products_to_fields_values pfv ON ( pfv.field_value_id = fvt.id )
right one:
JOIN products_to_fields_values pfv ON ( pfv.field_value_id = fvt.field_id )
I tried for your question, check this query
select fValue.field_id, f.field_name as field_name, fValue.field_value, fValue.id as field_value_id
from products_to_fields_values as productValue
left join fields_values as fValue on(fValue.field_id=productValue.field_value_id)
left join fields as f on (fValue.field_id=f.id)
left join products as p on (productValue.product_id=p.id)
where p.cid=1
I am quite sure the problem, i.e. the 'missing' records, is being caused by the 'group by' commands. It's a bit difficult to come up with a solution for you as I am not too sure what you are trying to achieve. The expected result you posted hasn't helped me much in this regard. The query you are trying to run is rather complex, and on a populated database is going to start running very, very slowly. As such it would suggest a better database design is required. If you can explain what you are trying to achieve I will gladly look at a solution for you.
Ignoring the limit and where clauses this is the query you need to use:
SELECT
fields.id AS field_id
, fields.field_name
, fields_values.field_value
, fields_values.id AS field_value_id
, COUNT(products.id) AS `count`
FROM fields_values
JOIN fields ON fields.id = fields_values.field_id
JOIN category AS field_category ON field_category.id = fields.cid
LEFT JOIN products_to_fields_values AS product_fields ON fields_values.id = product_fields.field_value_id
LEFT JOIN products ON products.id = product_fields.product_id
GROUP BY 1, 2, 3, 4;
This produces the following result:
| field_id | field_name | field_value | field_value_id | count |
+----------+------------+-----------------+----------------+-------+
| 1 | Processor | AMD Radeon | 4 | 0 |
| 1 | Processor | Intel Atom | 5 | 1 |
| 1 | Processor | Intel Pentium 3 | 1 | 1 |
| 2 | Display | 22 inch | 6 | 0 |
| 2 | Display | 27 inch | 2 | 1 |
| 3 | Brand | BMX | 3 | 1 |
In your where clause you can specify
WHERE category.id IN (1)
to get the result you want.
The mistake you were making (and a previous answer as well) was that you were joining the category through the product giving you the product_category instead of the field_category.
When you applied a where condition on the product_category, it removed all products not part of the set, so you would never get the count = 0

MySQL latest related record from more than one table

Assuming a main "job" table, and two corresponding "log" tables (one for server events and the other for user events, with quite different data stored in each).
What would be the best way to return a selection of "job" records and the latest corresponding log record (with multiple fields) from each of the two "log" tables (if there are any).
Did get some inspiration from: MySQL Order before Group by
The following SQL would create some example tables/data...
CREATE TABLE job (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` tinytext NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE job_log_server (
`id` int(11) NOT NULL AUTO_INCREMENT,
`job_id` int(11) NOT NULL,
`event` tinytext NOT NULL,
`ip` tinytext NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (id),
KEY job_id (job_id)
);
CREATE TABLE job_log_user (
`id` int(11) NOT NULL AUTO_INCREMENT,
`job_id` int(11) NOT NULL,
`event` tinytext NOT NULL,
`user_id` int(11) NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (id),
KEY job_id (job_id)
);
INSERT INTO job VALUES (1, 'Job A');
INSERT INTO job VALUES (2, 'Job B');
INSERT INTO job VALUES (3, 'Job C');
INSERT INTO job VALUES (4, 'Job D');
INSERT INTO job_log_server VALUES (1, 2, 'Job B Event 1', '127.0.0.1', '2000-01-01 00:00:01');
INSERT INTO job_log_server VALUES (2, 2, 'Job B Event 2', '127.0.0.1', '2000-01-01 00:00:02');
INSERT INTO job_log_server VALUES (3, 2, 'Job B Event 3*', '127.0.0.1', '2000-01-01 00:00:03');
INSERT INTO job_log_server VALUES (4, 3, 'Job C Event 1*', '127.0.0.1', '2000-01-01 00:00:04');
INSERT INTO job_log_user VALUES (1, 1, 'Job A Event 1', 5, '2000-01-01 00:00:01');
INSERT INTO job_log_user VALUES (2, 1, 'Job A Event 2*', 5, '2000-01-01 00:00:02');
INSERT INTO job_log_user VALUES (3, 2, 'Job B Event 1*', 5, '2000-01-01 00:00:03');
INSERT INTO job_log_user VALUES (4, 4, 'Job D Event 1', 5, '2000-01-01 00:00:04');
INSERT INTO job_log_user VALUES (5, 4, 'Job D Event 2', 5, '2000-01-01 00:00:05');
INSERT INTO job_log_user VALUES (6, 4, 'Job D Event 3*', 5, '2000-01-01 00:00:06');
One option (only returning 1 field from each table) would be to use nested sub-queries... but the ORDER BY will have to be done in separate queries to the GROUP BY (x2):
SELECT
*
FROM
(
SELECT
s2.*,
jlu.event AS user_event
FROM
(
SELECT
*
FROM
(
SELECT
j.id,
j.name,
jls.event AS server_event
FROM
job AS j
LEFT JOIN
job_log_server AS jls ON jls.job_id = j.id
ORDER BY
jls.created DESC
) AS s1
GROUP BY
s1.id
) AS s2
LEFT JOIN
job_log_user AS jlu ON jlu.job_id = s2.id
ORDER BY
jlu.created DESC
) AS s3
GROUP BY
s3.id;
Which actually seems to perform quite well... just not very easy to understand.
Or you could try to return and sort the log records in two separate sub-queries:
SELECT
j.id,
j.name,
jls2.event AS server_event,
jlu2.event AS user_event
FROM
job AS j
LEFT JOIN
(
SELECT
jls.job_id,
jls.event
FROM
job_log_server AS jls
ORDER BY
jls.created DESC
) AS jls2 ON jls2.job_id = j.id
LEFT JOIN
(
SELECT
jlu.job_id,
jlu.event
FROM
job_log_user AS jlu
ORDER BY
jlu.created DESC
) AS jlu2 ON jlu2.job_id = j.id
GROUP BY
j.id;
But this seems to take quite a bit longer to run... possibly because of the amount of records it's adding to a temporary table, which are then mostly ignored (to keep this short-ish, I've not added any conditions to the job table, which would otherwise be only returning active jobs).
Not sure if I've missed anything obvious.
How about the following SQL Fiddle. It produces the same results as both of your queries.
SELECT j.id, j.name,
(
SELECT s.event
FROM job_log_server s
WHERE j.id = s.job_id
ORDER BY s.id DESC
LIMIT 1
)AS SERVER_EVENT,
(
SELECT u.event
FROM job_log_user u
WHERE j.id = u.job_id
ORDER BY u.id DESC
LIMIT 1
)AS USER_EVENT
FROM job j
EDIT SQL Fiddle:
SELECT m.id, m.name, js.event AS SERVER_EVENT, ju.event AS USER_EVENT
FROM
(
SELECT j.id, j.name,
(
SELECT s.id
FROM job_log_server s
WHERE j.id = s.job_id
ORDER BY s.id DESC
LIMIT 1
)AS S_E,
(
SELECT u.id
FROM job_log_user u
WHERE j.id = u.job_id
ORDER BY u.id DESC
LIMIT 1
)AS U_E
FROM job j
) m
LEFT JOIN job_log_server js ON js.id = m.S_E
LEFT JOIN job_log_user ju ON ju.id = m.U_E