Get the other field value in case of NULL - mysql

CREATE TABLE itemmast (
cid INTEGER,
description varchar(30),
defaultprice DOUBLE);
INSERT INTO itemmast VALUES (1, 'Item 1', '0');
INSERT INTO itemmast VALUES (2, 'Item 2', '8');
INSERT INTO itemmast VALUES (3, 'Item 3', '5.5');
INSERT INTO itemmast VALUES (4, 'Item 4', '0');
INSERT INTO itemmast VALUES (5, 'Item 5', '59');
CREATE TABLE specialprice (
cid INTEGER,
username varchar(30),
newprice DOUBLE);
INSERT INTO specialprice VALUES (4, 'UserS', '10');
INSERT INTO specialprice VALUES (2, 'UserX', '115');
I want to get the value of [newprice] for specific user just in case its in the [specialprice] table, otherwise get the [defaultprice] from the [itemmast] table
So far I have this but not working as expected.
SELECT itemmast.*,newprice,
CASE specialprice.newprice
WHEN NULL
THEN itemmast.defaultprice
ELSE
specialprice.newprice
END AS itemprice
FROM itemmast
LEFT JOIN specialprice ON specialprice.cid = itemmast.cid
where itemmast.cid = '1' and username= 'UserS'

Change your CASE statement into
CASE WHEN specialprice.cid IS NULL
THEN itemmast.defaultprice
ELSE specialprice.newprice
END as RealPrice
SQLFiddle Demo
Or alternatively use COALESCE
COALESCE(specialprice.newprice, itemmast.defaultprice)
SQLFiddle Demo

Why not rather us IFNULL
•IFNULL(expr1,expr2)
If expr1 is not NULL, IFNULL() returns expr1; otherwise it returns
expr2. IFNULL() returns a numeric or string value, depending on the
context in which it is used.
mysql> SELECT IFNULL(1,0);
-> 1
mysql> SELECT IFNULL(NULL,10);
-> 10
mysql> SELECT IFNULL(1/0,10);
-> 10
mysql> SELECT IFNULL(1/0,'yes');
-> 'yes'
So in your example, something like
SELECT itemmast.*,
newprice,
IFNULL(specialprice.newprice, itemmast.defaultprice) itemprice
FROM itemmast LEFT JOIN
specialprice ON specialprice.cid = itemmast.cid

For a more generic approach, use IF(,,):
IF (specialprice.newprice IS NULL, specialprice.defaultprice, specialprice.newprice)
reference: http://dev.mysql.com/doc/refman/5.1/en/control-flow-functions.html#function_if

The other answers are OK, but the reason your SQL is not working is that null is never equal to anything, not even null, and your case statement is comparing the value of specialprice.newprice with null which will never be true.
The only test that is true for null is the special is null test, so you must use that:
case when specialprice.newprice is null ... else ... end
or use a built-in funciton:
select ifnull(specialprice.newprice, itemmast.defaultprice) ...

Related

How to speed up loop in MySQL, fill lines based on previous row value

My database is composed by individual job contracts. I am updating some information to enhance the quality of the data. More precisely, I am updating information regarding workers' residence codes. In the following image I am showing an example of my database in the following image (the .csv version could be found here).
While variables are explained here below.
id -----------> "Primary key" [indexed]
worker_id ----> "Id associated ot each individual/worker" [indexed]
dt_start -----> "Starting date of the job contract"
dt_end -------> "End date of the job contract"
cod_res ------> "Old residence code"
cod_res_rev --> "New residence code"
id_lag -------> "Previous id, if the 'worker_id' is the same" [indexed]
id_lead ------> "Subsequent id, it the 'worker_id' is the same" [indexed]
As you can notice, the column cod_res_rev is characterized is full of NULL values. This is because the reconstruction of the variable cod_res_rev with the updated residence values it was based solely on specific contracts (those for which the worker had had an actual change of residence - but this is redundant for the purposes of my question). Therefore, my goal is to fill each NULL value of the column cod_res_rev with the previous one, if not missing, until the next non-empty value is reached and continue like this for each worker. The result should be something like this.
I attempted to achieve my goal through the following procedure.
-- The loop is performed based on the maximum number of entries per worker in the database identified through the table 'max_count'.
drop table if exists max_count;
create table max_count
as select worker_id, count(*) n
from ml_arm
group by worker_id;
alter table max_count add unique index (worker_id);
DROP PROCEDURE IF EXISTS doiterate;
delimiter //
CREATE PROCEDURE doiterate()
BEGIN
DECLARE total INT unsigned DEFAULT 0;
WHILE total <= (select MAX(n) from max_count) DO
update ml_arm a
left outer join ml_arm b on a.id_lag = b.id
set a.cod_res_rev =
case
when a.cod_res_rev is NULL and a.worker_id = b.worker_id and b.cod_res_rev is not NULL
then b.cod_res_rev
else a.cod_res_rev
end;
SET total = total + 1;
END WHILE;
END//
delimiter ;
CALL doiterate();
However, I do not believe this is the optimal way to update my table. In fact, by database if composed by about 25 million of rows and the value from select MAX(n) from max_count is about 4,000. I kindly ask you for any suggestion on faster approaches to update my table. I am using MySQL 8.0.22. Thank you in advance.
Eventually, here below there is a command to create a sample table of my database with a bunch of entries.
drop table if exists ml_arm;
create table ml_arm (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
worker_id int,
dt_start date,
dt_end date,
cod_res varchar(50),
cod_res_rev varchar(50),
id_lag int,
id_lead int,
PRIMARY KEY (id)
);
insert into
ml_arm(id, worker_id, dt_start, dt_end, cod_res, cod_res_rev, id_lag, id_lead)
values
('12', '20', '2014-05-02', '2014-07-08', '', 'I040', NULL, '13'),
('13', '20', '2017-01-14', '2017-01-31', '', NULL, '12', '14'),
('14', '20', '2017-11-06', '2017-12-15', 'I040', NULL, '13', NULL),
('20', '29', '2014-11-24', '2017-02-11', '', 'N.D.', NULL, NULL),
('21', '42', '2016-01-22', '2016-05-05', 'G582', 'G582', NULL, NULL),
('23', '45', '2013-08-07', '2014-04-06', 'G582', 'G582', NULL, '24'),
('24', '45', '2014-05-07', '2014-05-10', 'G582', NULL, '23', NULL),
('25', '48', '2012-08-11', '2012-08-31', 'G582', 'G582', NULL, '26'),
('26', '48', '2013-08-10', '2013-08-31', 'G582', NULL, '25', NULL),
('53', '71', '2016-12-01', '2017-05-31', '', 'N.D.', NULL, '54'),
('54', '71', '2017-06-01', '2020-05-29', '', NULL, '53', '55'),
('55', '71', '2020-06-01', '2099-01-01', '', NULL, '54', NULL)
;
By following Gordon Linoff's answer on this thread it seems to work:
UPDATE ml_arm t1
JOIN (
SELECT id, #s:=IF(cod_res_rev IS NULL, #s, cod_res_rev) cod_res_rev
FROM (SELECT * FROM ml_arm ORDER BY id) r,
(SELECT #s:= NULL) t
) t2
ON t1.id = t2.id
SET t1.cod_res_rev = t2.cod_res_rev;

How to identify null values using json_extract mysql 8.0

What would be the right way to identify null value when using JSON_EXTRACT,
I want to use a case statement to identify null values in a json field and if the value is null replace it with another value in this case 1.
CREATE TABLE `Log` (
`change` json DEFAULT NULL
) ENGINE=InnoDB AUTO_INCREMENT=345 DEFAULT CHARSET=utf32 COLLATE=utf32_unicode_ci;
insert into `Log` (`change`) values (JSON_OBJECT('documentNumber', 2));
insert into `Log` (`change`) values (JSON_OBJECT('documentNumber', null));
select case
when isnull(JSON_EXTRACT(`change`, '$.documentNumber')) = 1 then '1'
else JSON_EXTRACT(`change`, '$.documentNumber')
end as 'result'
from `Log`;
For this query i am getting result,
result
2
null
but i am expecting,
result
2
1
dbfiddle
JSON 'null' literal is not NULL value.
See this demo:
CREATE TABLE `Log` (
`change` json DEFAULT NULL
) ENGINE=InnoDB AUTO_INCREMENT=345 DEFAULT CHARSET=utf32 COLLATE=utf32_unicode_ci;
insert into `Log` (`change`) values (JSON_OBJECT('documentNumber', 2));
-- JSON 'null'
insert into `Log` (`change`) values (JSON_OBJECT('documentNumber', null));
-- no key, this will produce regular NULL
insert into `Log` (`change`) values (JSON_OBJECT('documentNumberX', null));
select `change`,
case
when isnull(JSON_EXTRACT(`change`, '$.documentNumber')) = 1 then '1'
else JSON_EXTRACT(`change`, '$.documentNumber')
end as 'result',
JSON_EXTRACT(`change`, '$.documentNumber') IS NULL test1,
JSON_EXTRACT(`change`, '$.documentNumber') = CAST('null' AS JSON) test2,
case when JSON_EXTRACT(`change`, '$.documentNumber') = CAST('null' AS JSON)
then '1'
else JSON_EXTRACT(`change`, '$.documentNumber')
end as 'result2'
from `Log`;
fiddle

I cant get to execute the two statements in my trigger

I want both statements to be executed in my trigger. I can't get the following to work:
IF (flag = 'D') THEN
BEGIN
INSERT INTO logs (id, author_id, action_done, description, old_value, new_value, create_date)
VALUES (null, (SELECT id FROM gallery WHERE flag = 'D'), 'Delete', 'Gallery', (SELECT filename FROM gallery WHERE flag = 'D'), '', NOW());
DELETE FROM gallery WHERE flag = 'D';
END
END IF
I assume id field in logs table is a primary key and auto generated by system.
Try this:
IF (flag = 'D') THEN
BEGIN
INSERT INTO logs (author_id, action_done, description, old_value, new_value, create_date)
select id, 'Delete', 'Gallery', filename, '', NOW() from gallery where flag='D';
DELETE FROM gallery WHERE flag = 'D';
END
END IF
Your id should auto increment and thus should not be one of the values.
Presumably, your trigger is on the table gallery. If so, your trigger should be a "before" or "after" trigger and you can do:
IF (flag = 'D') THEN
BEGIN
INSERT INTO logs (author_id, action_done, description, old_value, new_value, create_date)
VALUES (old.id, 'Delete', 'Gallery', old.filename, '', NOW());
END;
END IF
There is no need to repeat the DELETE.

Error Code: 1136. Column count doesn't match value count at row 1 in a procedure which will add data in 3 tables?

CREATE PROCEDURE `sp_Add_Game` (IN Game_Name_ip VARCHAR(100),IN Genre_ip ENUM('Controllers', 'Extreme Sports', 'Action & Adventure', 'Racing', 'RPG', 'Baseball', 'Sports', 'Systems', 'Puzzle', 'Fighting', 'Strategy', 'FPS', 'Wrestling', 'Accessories', 'Soccer', 'Other', 'Football', 'Party', 'Arcade', 'Basketball', 'Simulation', 'Music'),
IN Rating_ip ENUM('1', '2', '3', '4', '5'),IN Platform_Name_ip ENUM('N64', 'NES', 'Super Nintendo', 'Gamecube', 'Wii', 'Playstation 1', 'Playstation 2', 'Playstation 3', 'Xbox', 'Xbox 360', 'Sega Genesis', 'Atari 2600', 'Gameboy Color', 'Gameboy Advance'),IN Completeness_Type_ip ENUM('B', 'I', 'C', 'BC', 'BI', 'IC', 'BIC'), IN Condition_ip ENUM('New', 'Mint', 'Very Good', 'Good', 'Acceptable', 'Poor'),
IN Purchase_Date_ip DATE,IN Purchase_Price_ip DECIMAL(4,2))
BEGIN
DECLARE Game_key int;
DECLARE MyCollection_Key int;
DECLARE Platform_Key int;
START TRANSACTION;
INSERT INTO videogame_collection_1.video_game(`Game_Name`, `Genre`, `Rating`) VALUES (Game_Name_ip, Genre_ip,Rating_ip,Rating_ip);
SET Game_key = LAST_INSERT_ID();
INSERT INTO videogame_collection_1.mycollection (`Completeness_Type`, `Condition`, `Purchase_Date`, `Purchase_Price`) VALUES (Completeness_Type_ip, Condition_ip, Purchase_Date_ip, Purchase_Price_ip);
SET MyCollection_Key = LAST_INSERT_ID();
INSERT INTO videogame_collection_1.platform(`Platform_Name`) VALUES (Platform_Name_ip);
SET Platform_Key = LAST_INSERT_ID();
INSERT INTO videogame_collection_1.video_game_platform_mycollection(`MyCollection_Id`, `Game_Id`, `Platform_Id`) VALUES (MyCollection_Key,Game_key,Platform_Key);
COMMIT;
END
I am getting the above error when I try to insert video game addition data from front end to Mysql Database. It will insert the data in 3 different tables. So, how can I implement this?
In this statement you are entering the Rating_ip value twice:
INSERT INTO videogame_collection_1.video_game(`Game_Name`, `Genre`, `Rating`) VALUES (Game_Name_ip, Genre_ip,Rating_ip,Rating_ip);
It should read:
INSERT INTO videogame_collection_1.video_game(`Game_Name`, `Genre`, `Rating`) VALUES (Game_Name_ip, Genre_ip,Rating_ip);
You receive the error because you were trying to insert 4 columns worth of data into an insert statement in which you were only specifying 3 columns.

Insert multiple rows into different tables

I want to insert multiple rows into different tables in my database. The statements are paired, where the second relies on data (specifically LAST_INSERT_ID()) from the first statement.
This code works, but I wonder if it can be done in a better way?
Is there a way to have it all in one single query?
<?php
mysql_query("
INSERT INTO wp_terms (term_id, name, slug, term_group)
VALUES ('', 'Test 1', 'test-1', '0')
");
mysql_query("
INSERT INTO wp_term_taxonomy (term_taxonomy_id, term_id, taxonomy, description, parent, count)
VALUES ('', LAST_INSERT_ID(), 'mycustomfield', 'Descripton 1', '0', '0')
");
mysql_query("
INSERT INTO wp_terms (term_id, name, slug, term_group)
VALUES ('', 'Test 2', 'test-2', '0')
");
mysql_query("
INSERT INTO wp_term_taxonomy (term_taxonomy_id, term_id, taxonomy, description, parent, count)
VALUES ('', LAST_INSERT_ID(), 'mycustomfield', 'Descripton 2', '0', '0')
");
mysql_query("
INSERT INTO wp_terms (term_id, name, slug, term_group)
VALUES ('', 'Test 3', 'test-3', '0')
");
mysql_query("
INSERT INTO wp_term_taxonomy (term_taxonomy_id, term_id, taxonomy, description, parent, count)
VALUES ('', LAST_INSERT_ID(), 'mycustomfield', 'Descripton 3', '0', '0')
");
mysql_query("
INSERT INTO wp_terms (term_id, name, slug, term_group)
VALUES ('', 'Test 4', 'test-4', '0')
");
mysql_query("
INSERT INTO wp_term_taxonomy (term_taxonomy_id, term_id, taxonomy, description, parent, count)
VALUES ('', LAST_INSERT_ID(), 'mycustomfield', 'Descripton 4', '0', '0')
");
?>
If it is a thing to reduce the call of mysql_query, Yes, There is a way but you have to use the mysqli for this.
The mysqli::multi_query can execute the multiple queries in once.
You also can create an INSERT trigger -
DELIMITER $$
CREATE TRIGGER trigger_wp_terms_insert
AFTER INSERT
ON wp_terms
FOR EACH ROW
BEGIN
INSERT INTO wp_term_taxonomy(term_taxonomy_id, term_id, taxonomy, description, parent, count)
VALUES ('', NEW.term_id, 'mycustomfield', CONCAT('Descripton ', SUBSTRING_INDEX(new.name, ' ', '-1')), '0', '0');
END
$$
DELIMITER ;
Then just add new rows into the wp_terms table using one bulk INSERT statement -
INSERT INTO wp_terms (term_id, name, slug, term_group) VALUES
('', 'Test 1', 'test-1', '0'),
('', 'Test 2', 'test-2', '0'),
('', 'Test 3', 'test-3', '0'),
('', 'Test 4', 'test-4', '0');
In my opinion, using a stored procedure would be one of the best ways to do it:
CREATE DEFINER=`root`#`localhost` PROCEDURE `InsertWpTermAndTaxonomy`
( IN paramWPT_id <<PlaceTheRightDataTypeHere>>
, IN paramWPT_name <<PlaceTheRightDataTypeHere>>
, IN paramWPT_slug <<PlaceTheRightDataTypeHere>>
, IN paramWPT_termgroup <<PlaceTheRightDataTypeHere>>
, IN paramWPTX_id <<PlaceTheRightDataTypeHere>>
, IN paramWPTX_taxonomy <<PlaceTheRightDataTypeHere>>
, IN paramWPTX_description <<PlaceTheRightDataTypeHere>>
, IN paramWPTX_parent <<PlaceTheRightDataTypeHere>>
, IN paramWPTX_count <<PlaceTheRightDataTypeHere>>
BEGIN
INSERT INTO wp_terms (term_id, name, slug, term_group)
VALUES (paramWPT_id, paramWPT_name, paramWPT_slug, paramWPT_termgroup)
INSERT INTO wp_term_taxonomy (term_taxonomy_id, term_id, taxonomy, description, parent, count)
VALUES (paramWPTX_id, LAST_INSERT_ID(), paramWPTX_taxonomy, paramWPTX_description, paramWPTX_parent, paramWPTX_count)
END
Then use code like this (though I choose PDO over MYSQL or MYSQLI):
# using an array which you can populate to contain all the
# necessary parameters...
$term_and_taxonomy_array_values = array('', 'Test 4', 'test-4', '0', '', 'mycustomfield', 'Descripton 4', '0', '0');
$sql = 'CALL InsertWpTermAndTaxonomy (?,?,?,?,?,?,?,?,?)';
$stmt = $pdo->prepare($sql);
foreach($term_and_taxonomy_array_values as $key => $value) {
$stmt->bindValue($key + 1, $value);
}
$stmt->execute();
Hope you can look into PDO, but mysqli should be able to do this as well. I coded the procedure so that it can be flexible enough to take whatever acceptable parameters for both tables, even though it seemed like you have constant 0s and ''s
If you want to further make this dynamic, say for example you can build your array of parameter arrays, then you'll just need one more foreach to wrap it up...
add an additional column to wp_terms, you can name the column as session_key, this is to store an unique value for each insertion, an example of unique value can be current timestamp concat with some WP session value
upon insertion, assign the session_key to an unique value and included into insert statement
combine all the insert into wp_terms into single insert ... values (...,session_key), (..., $session_key)
factor in error handling
after you have insert successfully, you can make use on the unique value you have created at step 2 to insert into wp_term_taxonomy select ... where session_key = $session_key
with this small trick, possibilities hitting an error is quite minimum