I am trying to create a procedure that will update all fruits status if a fruit has at least one critical attribute.
For example i have an apple,banana and an orange with many attributes. One of the attributes of apple, orange marked as "Yes" on critical. in that case with a single call of the procedure apple and orange will have status id pointing on "bad", and banana as "good".
create table `status` (
s_id int,
s_stat enum ("Bad", "Good", "Rotten"),
primary key (s_id)
) ENGINE = InnoDB;
create table fruits (
f_id int,
f_name varchar(10),
f_status int,
primary key (f_id),
foreign key (f_status) references `status`(s_id)
on update cascade
) ENGINE = InnoDB;
create table attributes (
a_id int,
a_description varChar(25),
a_critital enum ("Yes", "No"),
primary key(a_id)
) ENGINE = InnoDB;
create table fruits_attributes (
fa_f_id int,
fa_a_id int,
primary key(fa_f_id, fa_a_id),
foreign key (fa_f_id) references fruits(f_id)
on update cascade,
foreign key (fa_a_id) references attributes(a_id)
on update cascade
) ENGINE = InnoDB;
insert into `status` values
(1, "Bad"),
(2, "Good"),
(3, "Rotten")
;
insert into fruits values
(1, "Apple", null),
(2, "Banana", null),
(3, "Orange", null)
;
insert into attributes values
(1, "Yellow mark", "No"),
(2, "Cuts", "Yes"),
(3, "Dirt", "No")
;
insert into fruits_attributes values
(1, 1),
(1, 2),
(1, 3),
(2, 3),
(3, 2)
;
So you are prioritising "Bad" over "Good". For example, if one apple is "Good", another apple is "Bad", you would update it as "Bad". Let me know if this is not the case.
For MySQL 8.0 version, db<>fiddle
CREATE PROCEDURE updtfruit()
BEGIN
UPDATE fruits f
JOIN (
SELECT f_id, s_id, ROW_NUMBER() OVER(PARTITION BY f_id ORDER BY s_id) AS rn
FROM fruits_attributes fa
JOIN fruits f ON fa.fa_f_id = f.f_id
JOIN attributes a ON fa.fa_a_id = a.a_id
JOIN `status` s ON s.s_stat = CASE WHEN a.a_critital = "Yes" THEN "Bad"
WHEN a.a_critital = "No" THEN "Good"
END
) filter ON f.f_id = filter.f_id
SET f.f_status = filter.s_id
WHERE filter.rn = 1;
END
For MySQL older versions, db<>fiddle
CREATE PROCEDURE updtfruit2()
BEGIN
UPDATE fruits f
JOIN (
SELECT f_id, MIN(s_id) AS s_id
FROM fruits_attributes fa
JOIN fruits f ON fa.fa_f_id = f.f_id
JOIN attributes a ON fa.fa_a_id = a.a_id
JOIN `status` s ON s.s_stat = CASE WHEN a.a_critital = "Yes" THEN "Bad"
WHEN a.a_critital = "No" THEN "Good"
END
GROUP BY f_id
) filter ON f.f_id = filter.f_id
SET f.f_status = filter.s_id;
END
Related
I'm having two tables namely ds_message and ds_params, the first table contains the template and the second tables contains the key value pair
Table Structure: ds_message
_____________________________________________
id template
_____________________________________________
1 'Dear {a}, the price of {b} is {c}'
2 'Dear {i}, you selected the product {j}'
Schema:
CREATE TABLE `ds_message` (
`id` int NOT NULL,
`template` varchar(500) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='';
ALTER TABLE `ds_message`
ADD PRIMARY KEY (`id`);
INSERT INTO `ds_message` (`id`, `template`) VALUES
(1, 'Dear {a}, the price of {b} is {c}');
INSERT INTO `ds_message` (`id`, `template`) VALUES
(2, 'Dear {i}, you selected the product {j}');
Table Structure: ds_params
_________________________________________________
id message_id json_key json_value
_________________________________________________
1 1 a John
2 1 b bat
3 1 c $10
4 2 i Emma
5 2 j Jam
Schema:
CREATE TABLE `ds_params` (
`id` int NOT NULL,
`message_id` int NOT NULL,
`json_key` varchar(500) NOT NULL,
`json_value` varchar(500) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='';
ALTER TABLE `ds_params`
ADD PRIMARY KEY (`id`);
INSERT INTO `ds_params` (`id`, `message_id`, `json_key`, `json_value`) VALUES
(1, 1, 'a', 'John');
INSERT INTO `ds_params` (`id`, `message_id`, `json_key`, `json_value`) VALUES
(2, 1, 'b', 'bat');
INSERT INTO `ds_params` (`id`, `message_id`, `json_key`, `json_value`) VALUES
(3, 1, 'c', '$10');
INSERT INTO `ds_params` (`id`, `message_id`, `json_key`, `json_value`) VALUES
(4, 2, 'i', 'Emma');
INSERT INTO `ds_params` (`id`, `message_id`, `json_key`, `json_value`) VALUES
(5, 2, 'j', 'Jam');
I need to replace the keys (for example {a} => John) in the ds_message table.
I tried the following code,
UPDATE ds_message AS t
INNER JOIN ds_params m ON m.message_id = t.id
SET t.template = REPLACE(t.template, CONCAT('{', m.json_key , '}'), m.json_value);
Once I executed the code I'm getting the output like this, only the first key gets replaced, remaining keys not get update.
_____________________________________________
id template
_____________________________________________
1 Dear John, the price of {b} is {c}
2 Dear Emma, you selected the product {j}
Kindly assist me how to do this.
We have 2 options.
First is using while loop.
SET #n = 0;
SELECT COUNT(*) FROM ds_params INTO #n;
SET #i=0;
WHILE #i<#n DO
UPDATE ds_message AS t
INNER JOIN ds_params m ON m.message_id = t.id AND m.id = #i
SET t.template = REPLACE(t.template, CONCAT('{', m.json_key , '}'), m.json_value)
SET #i = #i + 1;
END WHILE;
2nd Option is this. If you have a fix json_key
UPDATE ds_message AS t
LEFT JOIN ds_params a ON a.message_id = t.id and a.json_key='a'
LEFT JOIN ds_params b ON b.message_id = t.id and b.json_key='b'
LEFT JOIN ds_params c ON c.message_id = t.id and c.json_key='c'
LEFT JOIN ds_params i ON i.message_id = t.id and i.json_key='i'
LEFT JOIN ds_params j ON j.message_id = t.id and j.json_key='j'
SET t.template = REPLACE(REPLACE(REPLACE(
REPLACE(REPLACE(t.template, CONCAT('{', IFNULL(a.json_key, '') , '}')
, IFNULL(a.json_value, '')),
CONCAT('{', IFNULL(b.json_key, '') , '}'), IFNULL(b.json_value, '')),
CONCAT('{', IFNULL(c.json_key, '') , '}'), IFNULL(c.json_value, '')),
CONCAT('{', IFNULL(i.json_key, '') , '}') ,IFNULL(i.json_value, '')),
CONCAT('{', IFNULL(j.json_key, '') , '}') ,IFNULL(j.json_value, ''));
You need to perform loop in this update. Since there is no fix key value pair so it is better to find the max key value pair to perform loop.
After that loop through each record using CURSOR to perform the update to replace your key with corresponding value. Since you already find out the max value of key pair so it will perform only till the max limit of loop for your query.
For reference you may find this link for details on CURSOR.
DECLARE a, b VARCHAR(10);
DECLARE cur1 CURSOR FOR SELECT DISTINCT json_key,json_value
FROM ds_params.t1;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO a, b;
IF done THEN
LEAVE read_loop;
END IF;
UPDATE ds_message AS t
INNER JOIN ds_params m ON m.message_id = t.id
SET t.template = REPLACE(t.template, CONCAT('{', m.json_key , '}'), m.json_value)
WHERE M.JSON_KEY = b ;
END LOOP;
CLOSE cur1;
I am not sure about syntax but you may get the idea of implementing the same. If anything in syntax is not correct then kindly update the same.
I recently upgraded to MySQL 5.7.22 and my query stopped working. I have two tables "items" and "packages" where I'm trying to output a row for each item including a column for the package with the minimum price per unit, but ignore packages that have a price per unit set to 0.
Here's a minimal sample of tables and data:
CREATE TABLE `items` (
`id` int(11) NOT NULL
);
CREATE TABLE `packages` (
`item_id` int(11) NOT NULL,
`price_per_unit` float(16,6) DEFAULT 0
);
INSERT INTO `items` (`id`) VALUES
(1),
(2),
(3);
INSERT INTO `packages` (`item_id`, `price_per_unit`) VALUES
(1, 0.45),
(1, 0),
(1, 0.56),
(1, 0.34);
Here's the query:
SELECT
*
FROM
(
SELECT
items.id,
NULLIF(pkgs.ppu, 0) AS mppu
FROM
items
LEFT JOIN
(
SELECT
item_id,
price_per_unit AS ppu
FROM
packages
) AS pkgs ON pkgs.item_id = items.id
ORDER BY
IFNULL(mppu, 9999)
) X
GROUP BY
X.id
I was setting the zero values to null and then bumping their values to be much higher during the ordering. There must be a better way (especially since this method doesn't work any longer).
The expected output for this data is:
id mppu
1 0.34
2 null
3 null
I think your query is a bit too complex. What about this?
SELECT i.id,IFNULL(Min(p.price_per_unit), 'NONE')
FROM items i
LEFT JOIN packages p
ON ( i.id = p.item_id )
WHERE p.price_per_unit > 0
OR p.price_per_unit IS NULL
GROUP BY i.id
See this fiddle. I used this data:
INSERT INTO `items` (`id`) VALUES
(1),(2),(3);
INSERT INTO `packages` (`item_id`, `price_per_unit`) VALUES
(1, 0.45),
(1, 0),
(1, 0.56),
(1, 0.34),
(2, 9.45),
(2, 0),
(2, 0.56),
(2, 0.14);
And got this result:
id IFNULL(min(p.price_per_unit),'None')
1 0.340000
2 0.140000
3 None
Agree with GL,
SELECT * FROM GROUP BY
is not predictable .
i will rewrite the query with :
SELECT a.*,b.min_price_per_unit
FROM items a
LEFT JOIN (
SELECT item_id
,min(CASE
WHEN price_per_unit = 0
THEN 9999
ELSE price_per_unit
END) AS min_price_per_unit
FROM packages
GROUP BY item_id
) b ON a.id = b.item_id;
I have the following set of data :
id changeType dateTimeStamp userName
1 insert 07/02/2015 0:15:53 john
2 update 07/02/2015 0:15:52 adriana
3 insert 07/02/2015 0:15:51 john
4 update 07/02/2015 0:15:50 john
5 update 07/01/2015 22:46:57 denise
I am trying to retrieve users who overwrites each other's data that made a change within 5 minutes of each other. For this particular sets of data, I should get IDs 1, 2, and 3.
I tried :
SELECT *
FROM tblChanges v1
INNER JOIN tblChanges v2
ON v2.userName <> v1.userName
AND v1.dateTimeStamp <> v2.dateTimeStamp
WHERE ABS( TIMESTAMPDIFF(MINUTE, v1.dateTimeStamp, v2.dateTimeStamp ) ) <= 5
This also retrieves ID=4 which I don't need. Any help?
thanks in advance.
EDIT : Output would be
id changeType dateTimeStamp userName
1 insert 07/02/2015 0:15:53 john
2 update 07/02/2015 0:15:52 adriana
3 insert 07/02/2015 0:15:51 john
EDIT: here are the DDL / DML
CREATE TABLE `tblChanges` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`changeType` VARCHAR(50) DEFAULT NULL,
`dateTimeStamp` DATETIME NOT NULL ON UPDATE CURRENT_TIMESTAMP,
`userName` VARCHAR(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1 ;
INSERT INTO tblChanges ( id, changeType, dateTimeStamp, userName )
VALUES (1, 'insert' , '2015-07-02 12:15:53', 'john' ) ;
INSERT INTO tblChanges ( id, changeType, dateTimeStamp, userName )
VALUES (2, 'update' , '2015-07-02 12:15:52', 'adriana' ) ;
INSERT INTO tblChanges ( id, changeType, dateTimeStamp, userName )
VALUES (3, 'insert' , '2015-07-02 12:15:51', 'john' ) ;
INSERT INTO tblChanges ( id, changeType, dateTimeStamp, userName )
VALUES (4, 'update' , '2015-07-02 12:15:50', 'john' ) ;
INSERT INTO tblChanges ( id, changeType, dateTimeStamp, userName )
VALUES (5, 'update' , '2015-07-01 22:46:57', 'denise' ) ;
Your join is selecting records that occur both before and after the current record, instead of only after.
Suggest:
SELECT *
FROM tblChanges v1
INNER JOIN tblChanges v2
ON v2.userName <> v1.userName
AND v2.dateTimeStamp BETWEEN v1.dateTimeStamp AND DATE_ADD(v1.dateTimeStamp, INTERVAL 5 MINUTE)
I am trying to construct a query that will allow me to "filter" on pairs of columns for particular criteria. I need to be able to construct multiple filters for the same given pair. The end result should only return instances that have data for the case where all filters are applied.
I constructed a trivial example demonstrating what I would like to be able to do.
Using the follow table definition:
DROP TABLE IF EXISTS foo;
CREATE TEMPORARY TABLE `foo` (
`ID` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`Index` INT UNSIGNED NOT NULL,
`Header` VARCHAR(50) NOT NULL,
`Value` VARCHAR(255) NOT NULL,
PRIMARY KEY (`ID`),
UNIQUE INDEX `ID_UNIQUE` (`ID` ASC));
INSERT INTO `foo` (`Index`, `Header`, `Value`)
VALUES
(0, 'Header_1', 'a'),
(0, 'Header_2', 'b'),
(1, 'Header_1', 'a'),
(1, 'Header_2', 'c');
I would like a query that would return the following, given that you are looking for the case where 'Header_1' == 'a' and 'header_2' == 'b':
Index | Header | Value
------------------------
0 | Header_1 | a
0 | Header_2 | b
My current attempt is as follows:
SELECT `Index`, `Header`, `Value` FROM `foo`
WHERE (
(`Header` = 'Header_1') AND (`Value` = 'a')
OR (
(`Header` = 'Header_2') AND (`Value` = 'b')
)
)
GROUP BY `Header`, `Value`
HAVING COUNT(DISTINCT `Index`) = 2
ORDER BY `Index`, `Header`;
That code returns the following:
Index | Header | Value
------------------------
0 | Header_1 | a
I am missing one of my return rows. How can I restructure the query to return all of the matching rows?
Note that I declared the table as a temporary table. This is important, as I am working with temporary tables, and they have special restrictions to keep in mind (namely not being able to open it more than once in the same statement).
Your query returns only header_1 because the clause:
HAVING COUNT(DISTINCT `Index`) = 2
is only correct for Header_1.
Header_2 has count=1, therefore removed from the end result.
To get a clearer picture of what i say use:
SELECT `Index`, `Header`, `Value`, COUNT(DISTINCT `Index`) FROM `foo`
WHERE (
(`Header` = 'Header_1') AND (`Value` = 'a')
OR (
(`Header` = 'Header_2') AND (`Value` = 'b')
)
)
GROUP BY `Header`, `Value`
ORDER BY `Index`, `Header`;
and take a look at the last column.
I couldn't figure out how to do this with only the one temporary table. I'm not happy with this result, but at least it works.
DROP TABLE IF EXISTS `foo2`;
CREATE TEMPORARY TABLE `foo2` (
SELECT `Index` FROM `foo`
WHERE (
(`Header` = 'Header_1') AND (`Value` = 'a')
OR (
(`Header` = 'Header_2') AND (`Value` = 'b')
)
)
GROUP BY `Index`
HAVING COUNT(DISTINCT `Header`) = 2
);
SELECT DISTINCT t1.`Index`, t1.`Header`, t1.`Value` FROM `foo` t1
INNER JOIN `foo2` t2 ON t2.`Index` = t1.`Index`
ORDER BY t1.`Index`, t1.`Header`;
How about...
SELECT `index`
FROM foo
WHERE (header,value) IN (('header_1','a'))
OR (header,value) IN (('header_2','b'))
GROUP
BY `index`
HAVING COUNT(*) = 2;
SELECT c.name
FROM Customer c
WHERE NOT EXISTS(SELECT w.WID
FROM Woker w
WHERE NOT EXISTS(SELECT la
FROM look_after la
WHERE la.CID = c.CID
AND la.WID = w.WID));
I dont know what the code means... Could anyone tell me broadly what the code do? C is a Customer, who will looked after from a Worker.
The query selects customers that are looked after by all workers.
The double not exists is a way to implement relational division.
As an illustration to Andomar's excellent answer an example:
-- Some test data
DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;
CREATE TABLE persons
( person_id INTEGER NOT NULL PRIMARY KEY
, pname varchar
);
INSERT INTO persons( person_id, pname ) VALUES
(1 , 'Bob' ) ,(2 , 'Alice' ) ,(3 , 'Carol' )
;
CREATE TABLE movies
( movie_id INTEGER NOT NULL PRIMARY KEY
, mname varchar
);
INSERT INTO movies( movie_id, mname ) VALUES
(1, 'The Blues brothers' ), (2, 'Modern Times' ), (3, 'The Sound of Music' )
,(4, 'Amadeus' ), (5, 'Never say Never' )
;
-- people that have seen a particular movie
CREATE TABLE person_movie
( person_id INTEGER NOT NULL
, movie_id INTEGER NOT NULL
, PRIMARY KEY ( person_id, movie_id)
);
INSERT INTO person_movie( person_id, movie_id) VALUES
(1 ,5 ) ,(1 ,1 )
,(2 ,5 ) ,(2 ,4 ) ,(2 ,1 ) ,(2 ,3 ) ,(2 ,2 )
,(3 ,1 ) ,(3 ,3 )
;
-- Find the people that have seen ALL the movies
-- This is equivalent to:
-- Find persons for whom NO movie exists that (s)he has NOT seen
SELECT * FROM persons p
WHERE NOT EXISTS (
SELECT * FROM movies m
WHERE NOT EXISTS (
SELECT * FROM person_movie pm
WHERE pm.movie_id = m.movie_id
AND pm.person_id = p.person_id
)
);
-- similar: Find the movies that have been seen by ALL people
SELECT * FROM movies m
WHERE NOT EXISTS (
SELECT * FROM persons p
WHERE NOT EXISTS (
SELECT * FROM person_movie pm
WHERE pm.movie_id = m.movie_id
AND pm.person_id = p.person_id
)
);
Results:
person_id | pname
-----------+-------
2 | Alice
(1 row)
movie_id | mname
----------+--------------------
1 | The Blues brothers
(1 row)