mysql LEFT JOIN not acting like left join - mysql

Here's my problem. I have to get a list of questions and their answers in the same query. If the answer is older than 30 days, I want to get an empty reply, but I still want the question:
Im trying to do a left join, but if there is no match the question is still not showing up. You can see the schema and my select in this sqlfiddle and also down here:
http://sqlfiddle.com/#!9/a88184/1
SELECT p.*
, r.nota
, r.replied
FROM preguntas p
LEFT
JOIN respuestas r
ON p.id = r.id_pregunta
AND r.uuid_user ="f6912e4bb23130b9"
WHERE r.replied > DATE_SUB(NOW(),INTERVAL 30 DAY)
AND p.id_formulario = "1"
AND activo ="1"
ORDER
BY orden ASC
Schema
CREATE TABLE `preguntas` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`id_formulario` INT(11) NOT NULL,
`pregunta` TEXT NULL COLLATE 'utf8_spanish_ci',
`opcional` TINYINT(1) NOT NULL DEFAULT '0',
`activo` TINYINT(1) NOT NULL DEFAULT '0',
`orden` INT(11) NOT NULL,
PRIMARY KEY (`id`))
COLLATE='utf8_spanish_ci'
ENGINE=InnoDB
ROW_FORMAT=DYNAMIC
AUTO_INCREMENT=302
;
CREATE TABLE `respuestas` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`intento` INT(11) NOT NULL DEFAULT '1',
`id_formulario` INT(11) NOT NULL,
`id_pregunta` INT(11) NULL DEFAULT NULL,
`uuid_user` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8_spanish_ci',
`nota` INT(11) NULL DEFAULT NULL,
`replied` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`))
COLLATE='utf8_spanish_ci'
ENGINE=InnoDB
ROW_FORMAT=DYNAMIC
AUTO_INCREMENT=1259;
INSERT INTO `preguntas` (`id`, `id_formulario`, `pregunta`, `opcional`, `activo`, `orden`) VALUES (126, 1, 'INICIATIVA PERSONAL', 0, 1, 1);
INSERT INTO `preguntas` (`id`, `id_formulario`, `pregunta`, `opcional`, `activo`, `orden`) VALUES (139, 1, 'TENER RAPIDEZ Y AGILIDAD', 0, 1, 5);
INSERT INTO `respuestas` (`id`, `intento`, `id_formulario`, `id_pregunta`, `uuid_user`, `nota`, `replied`) VALUES (174, 1, 1, 126, 'f6912e4bb23130b9', 4, '2019-05-23 18:08:15');
INSERT INTO `respuestas` (`id`, `intento`, `id_formulario`, `id_pregunta`, `uuid_user`, `nota`, `replied`) VALUES (175, 1, 1, 139, 'f6912e4bb23130b9', 4, '2019-04-03 18:08:15');
Current result:
id id_formulario pregunta opcional activo orden nota replied
126 1 INICIATIVA PERSONAL false true 1 4 2019-05-23T18:08:15Z
Expected result:
id id_formulario pregunta opcional activo orden nota replied
126 1 INICIATIVA PERSONAL false true 1 4 2019-05-23T18:08:15Z
139 1 TENER RAPIDEZ Y AGILIDAD false true 5 (empty) (empty)

Putting the left table's columns in where clause effectively turns the left join into an inner join.
To prevent that, Move the condition to join:
SELECT p.*, r.nota, r.replied FROM preguntas p
LEFT JOIN respuestas r ON p.id = r.id_pregunta
AND r.uuid_user ="f6912e4bb23130b9" and r.replied > DATE_SUB(NOW(),INTERVAL 30 DAY)
where p.id_formulario = "1" AND activo ="1" ORDER BY orden ASC
sqlfiddle

The reason you don't get the results you want is the WHERE clause.
Put the conditions in the ON clause:
SELECT p.*, r.nota, r.replied FROM preguntas p
LEFT JOIN respuestas r
ON p.id = r.id_pregunta AND r.uuid_user ="f6912e4bb23130b9"
AND r.replied > DATE_SUB(NOW(),INTERVAL 30 DAY)
AND p.id_formulario = "1" AND activo ="1"
ORDER BY orden ASC
This condition:
r.replied > DATE_SUB(NOW(),INTERVAL 30 DAY)
removes the row from the results when it is placed in the WHERE clause, this is why you don't see it. By placing the condition in the ON clause of the join it is still there although there is no match in the other table.
For the other 2 conditions:
p.id_formulario = "1" AND activo ="1"
I'm not sure if you want them to reduce the results, so keep them in WHERE.

Related

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

How to join two table to get day sale

I want to join two table with some condition
Table 1
CREATE TABLE IF NOT EXISTS `payment` (
`paymentcode` int(6) NOT NULL,
`date` int(3) unsigned NOT NULL,
`amount` varchar(200) NOT NULL,
`customer` varchar(200) NOT NULL,
`store` varchar(200) NOT NULL
)
('2', '20190120', '10050','C1','A'),
('2', '20190120', '10050','c2','A'),
('6', '20190120', '9050','c3','A'),
('4', '20190120', '9045','c4','B'),
('6', '20190121', '10050','c5','B'),
('2', '20190121', '20050','c6','A');
Table 2
CREATE TABLE IF NOT EXISTS `customer` (
`code` int(6) NOT NULL,
`name` int(3) NOT NULL,
)
( 'C1','Customer1'),
( 'c2','Customer2'),
( 'c3','Customer3'),
( 'c4','Customer4'),
( 'c5','Customer5'),
( 'c6','Customer6');
select a.date,a.Paymentcode,a.store,b.Amount as document_total
from
(select date ,Paymentcode,store,sum(amount) from payment
group by date ,Paymentcode,store
) a
join
(select date ,store, sum(amount)as Amount from Payment
group by date ,store) b
on a.date = b.date and a.store = b.store
From above query I can fetch below value:
date paymentcode Amount documet_total
20190120 2 20100 29150
20190120 4 9045 9045
20190120 6 18095 29150
20190121 4 20050 20050
20190121 2 10050 10050
This was the query I was trying. Now I want to JOIN customer table to get customer code if Paymentcode='2' else need to take store value
My expected out is below:
date paymentcode Amount customer_type document_total
20190120 2 10050 C1 29150
20190120 2 10050 C2 29150
20190120 4 9045 B 9045
20190120 6 18095 A 29150
20190121 4 20050 B 20050
20190121 2 10050 C6 10050
Where I need to calculate amount based on date,store,customer_type and Paymentcode, customer_type should customercode it paymentcode='2' else store code, document_total is based on date,store.
Please advice me how to join these table and get the output

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, conditionally adding columns

I have two tables, the first holds data about patients, it looks like this.
id patient sex impact
------------------------------------------
1 Bill Jones male .1
2 Sarah Smith female .4
The second holds "multipliers". These multipliers will be used to multiply the impact in the table above.
id type type_value multiplier
-----------------------------------------------
1 patient Bill Jones .5
2 sex male .3
3 sex male .8
4 sex female .7
I am trying to run a query that will return the following:
patient patient_total sex_total new_impact
-------------------------------------------------------------------------------
Bill Jones .5 1.1 .16
Sarah Smith 0 .7 .28
Where the new impact is the (patient_total + sex_total) * impact for each patient.
Here are the create statements:
--
-- Table structure for table `impact`
--
CREATE TABLE IF NOT EXISTS `impact` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`patient` varchar(20) NOT NULL,
`sex` varchar(7) NOT NULL,
`impact` float NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
--
-- Dumping data for table `impact`
--
INSERT INTO `impact` (`id`, `patient`, `sex`, `impact`) VALUES
(1, 'Bill Jones', 'male', 0.1),
(2, 'Sarah Smith', 'female', 0.4);
--
-- Table structure for table `multipliers`
--
CREATE TABLE IF NOT EXISTS `multipliers` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`type` varchar(20) NOT NULL,
`type_value` varchar(60) NOT NULL,
`multiplier` float NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=14 ;
--
-- Dumping data for table `multipliers`
--
INSERT INTO `multipliers` (`id`, `type`, `type_value`, `multiplier`) VALUES
(1, 'patient', 'Bill Jones', 0.5),
(2, 'sex', 'male', 0.3),
(3, 'sex', 'male', 0.8),
(13, 'sex', 'female', 0.7);
I have tried several iterations of the following query, but can't get it to work :/
Updated quer - working for sex_total column, not for patient_total column :/
select p.patient, ifnull(sum(ipatient.multiplier), 0) as patient_total, ifnull(sum(isex.multiplier), 0) as sex_total, (ifnull(sum(ipatient.multiplier), 0) + ifnull(sum(isex.multiplier), 0) * p.impact) as new_impact
from impact p
left outer join multipliers ipatient
on ipatient.type = 'patient' and ipatient.type_value = p.patient
left outer join multipliers isex
on isex.type = 'sex' and isex.type_value = p.sex
group by p.patient
Could someone please help?
Thanks
You just need to join in the impact rows and then aggregate the results. The following does two joins, one for each type of impact:
select p.patient, sum(ipatient.multiplier) as patient_total,
sum(isex.multiplier) as sex_total,
(sum(ipatient.multiplier) * sum(isex.multiplier) * p.imact
) as new_impact
from impact p left outer join
multipliers ipatient
on ipatient.type = 'patient' and ipatient.type_value = p.patient left outer join
multipliers isex
on isex.type = 'sex' and isex.type_value = p.sex
group by p.patient;
You might need a coalesce() if there might be some rows that have no matches for either the patient name or sex.
EDIT:
Dumb. Dumb. Dumb. The above fails because the isex multiplies the rows, so it affects the sum() of ipatient. This version works:
select p.patient,
sum(case when m.type = 'patient' then m.multiplier else 0 end) as patient_total,
sum(case when m.type = 'sex' then m.multiplier else 0 end) as sex_total,
(sum(m.multiplier)* p.impact
) as new_impact
from impact p left outer join
multipliers m
on m.type = 'patient' and m.type_value = p.patient or
m.type = 'sex' and m.type_value = p.sex
group by p.patient;
You can see it here.
If you don't like all the decimal places from the float, you can switch to a numeric/decimal data type.
This query will give you the columns you need, but you'll have to do the multiplication yourself.
SELECT i.id, i.patient, i.impact,
(SELECT IFNULL(SUM(multiplier), 0) FROM multipliers
WHERE type='patient' AND type_value=i.patient) `patient_total`,
(SELECT IFNULL(SUM(multiplier), 0) FROM multipliers
WHERE type='sex' AND type_value=i.sex) `sex_total`
FROM impact i

Mysql order by with grouping

Hello everyone today i got in to a problem..
first thing i have a two table each table i have "product_seq_id" column and i joined table using the same "product_seq_id"
in the second table there are multiple rows for "product_seq_id" i want only one with below condition
table2.date_start not be null
table2.date_start is equal to '0000-00-00' or table2.date_start <= CURDATE()
table2.date_end is equal to '0000-00-00' or table2.date_start >= CURDATE()
get highest table2.priority if 2 or more rows match on the same day
I have already did some work.. but the problem is in that it's not taking highest priority number while ordering the column with grouped
//My Query
SELECT
psp . *, pcp . *
FROM
sk_product_category_path pcp
left join
sk_product_special_price psp ON (psp.product_seq_id = pcp.product_seq_id)
where
pcp.category_seq_id = 146
AND psp.product_seq_id IS NOT NULL
AND CASE
WHEN
psp.date_start IS NOT NULL
THEN
(psp.date_start = '0000-00-00'
OR psp.date_start <= CURDATE())
AND (psp.date_end = '0000-00-00'
OR psp.date_end >= CURDATE())
ELSE 1 = 1
END
group by psp.product_seq_id
order by psp.priority desc
Result Came for above code:
# product_special_price_seq_id, product_special_price, date_start, date_end, priority, product_seq_id, product_category_path_seq_id, product_seq_id, category_seq_id
2309 123123 0000-00-00 0000-00-00 0 3196 1 3196 146
2307 12313 0000-00-00 0000-00-00 0 3197 3 3197 146
Result I wanted:
# product_special_price_seq_id, product_special_price, date_start, date_end, priority, product_seq_id, product_category_path_seq_id, product_seq_id, category_seq_id
2309 12200 0000-00-00 0000-00-00 1 3196 2 3196 146
2307 12313 0000-00-00 0000-00-00 0 3197 3 3197 146
// Table Data
CREATE TABLE IF NOT EXISTS `sk_product_category_path` (
`product_category_path_seq_id` int(11) NOT NULL AUTO_INCREMENT,
`product_seq_id` int(11) NOT NULL,
`category_seq_id` int(11) NOT NULL,
PRIMARY KEY (`product_category_path_seq_id`),
UNIQUE KEY `product_seq_id` (`product_seq_id`,`category_seq_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
INSERT INTO `sk_product_category_path` (`product_category_path_seq_id`, `product_seq_id`, `category_seq_id`) VALUES
(1, 3196, 146),
(2, 3197, 146),
(3, 3198, 146);
CREATE TABLE IF NOT EXISTS `sk_product_special_price` (
`product_special_price_seq_id` int(11) NOT NULL AUTO_INCREMENT,
`product_special_price` bigint(20) DEFAULT NULL,
`date_start` date DEFAULT NULL,
`date_end` date DEFAULT NULL,
`priority` int(11) DEFAULT NULL,
`product_seq_id` int(11) NOT NULL,
PRIMARY KEY (`product_special_price_seq_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
INSERT INTO `sk_product_special_price` (`product_special_price_seq_id`, `product_special_price`, `date_start`, `date_end`, `priority`, `product_seq_id`) VALUES
(1, 12313, '0000-00-00', '0000-00-00', 0, 3197),
(2, 12200, '2014-02-11', '2014-02-11', 1, 3197),
(3, 123123, '0000-00-00', '0000-00-00', 0, 3196);
During GROUP BY in MySQL, it picks first matching row for each group unless you are using an aggregate function. The first matching need not be always row with min(id) .
The possible query should be something like :
SELECT t.*
from table_name t
inner join (
select min(id) as id
from table_name t
group by col) as s
on s.id = t.id
Please find the below query.. let me know is this is your requirement?
SELECT *
FROM sk_product_special_price pspo
WHERE pspo.priority IN(SELECT MAX(psp.priority)
FROM sk_product_special_price psp
JOIN sk_product_category_path pcp
ON(pcp.product_seq_id=psp.product_seq_id)
WHERE psp.date_start IS NOT NULL
AND psp.date_start BETWEEN '0000-00-00' AND CURDATE()
AND (psp.date_end>=CURDATE() OR psp.date_end='0000-00-00')
AND pcp.product_seq_id=pspo.product_seq_id);
I have updated the end date "'2014-02-11" to "2014-02-12" for my code to fetch end date >=today's date.
this query will return the table2 details i.e table sk_product_special_price for each all the product based on the priyority values.
the output will be
product_special_price_seq_id, product_special_price, date_start, date_end, priority, product_seq_id
2, 12200, '2014-02-11', '2014-02-12', 1, 3197
3, 123123, '0000-00-00', '0000-00-00', 0, 3196