Missing values percentage in MYSQL - mysql

I want to find the missing values percentage for each column in table name passed in missing_data_perc function parameter.
Delimiter $$
CREATE PROCEDURE missing_data_perc(IN tableName VARCHAR(30))
BEGIN
DECLARE i int default 0;
DECLARE n int default 0;
DECLARE columnName VARCHAR(30);
DECLARE perc int;
SET #tname = tableName;
SET #perc = 0;
DROP TEMPORARY TABLE IF EXISTS missing_data;
Create TEMPORARY TABLE missing_data (
data_name VARCHAR(50),
missing_data_percentage int
);
SELECT count(*) from information_schema.columns where table_name = tableName into n;
SET i=0;
while i < n Do
-- Select column_name from information_schema.columns where table_name = tableName limit i,1 into columnName;
-- Set #Q3 = CONCAT('SELECT 1-count(*)/count(columnName) from ? into ?');
-- PREPARE Stmt3 from #Q3;
-- EXECUTE Stmt3 using #tname, #perc;
-- SET perc = #perc;
SELECT 1-count(*)/count(columnName) from tableName into perc;
INSERT into missing_data VALUES (columnName, perc);
SET i = i+1;
End While;
SELECT * from missing_data;
END; $$
Delimiter ;
CALL missing_data_perc("deliveries");
Below line is giving me error:
SELECT 1-count(*)/count(columnName) from tableName into perc;
But if i change it to below statement then i works,
SELECT 1-count(*)/count(columnName) from deliveries into perc;

Related

MySQL Procedure variable receiving null on count(*)

Why when I do Select count(*) From table1 I receive 300 but if I do SELECT end = COUNT(*) FROM table1; returns null
Here is the fiddle example https://dbfiddle.uk/ZHzoaztV
code snippet:
CREATE TABLE table1(
start int NOT NULL,
id int PRIMARY KEY AUTO_INCREMENT,
counter int NOT NULL,
difference int NOT NULL,
end int NOT NULL
);
CREATE PROCEDURE doWhile()
BEGIN
DECLARE i INT DEFAULT 1;
DECLARE start INT DEFAULT 120;
DECLARE counter INT DEFAULT 1;
DECLARE end INT DEFAULT 300;
WHILE (i <= end) DO
INSERT INTO table1 VALUES (start,null,counter,start+counter,end);
SET i = i+1;
SET counter = counter+1;
END WHILE;
END;
CALL doWhile();
SELECT * FROM table1;
CREATE PROCEDURE insertMore()
BEGIN
DECLARE start INT;
DECLARE counter INT DEFAULT 1;
DECLARE end INT;
SELECT end = COUNT(*) FROM table1;
SELECT start = MAX(id)+1 FROM table1;
-- SELECT COUNT(*) FROM table1;
WHILE (counter <= end) DO
INSERT INTO table1 VALUES (start,null,counter,start+counter,end);
SET counter = counter+1;
END WHILE;
END;
CALL insertMore();
SELECT * FROM table1;
I expected to return 300, so hopefully my function should do it right
You have a problem with start and end Variable
Can you try this :
CREATE PROCEDURE insertMore()
BEGIN
DECLARE start INT;
DECLARE counter INT DEFAULT 1;
DECLARE end INT;
SELECT COUNT(*) into end FROM table1;
SELECT max(id)+1 into start FROM table1;
-- SELECT COUNT(*) FROM table1;
WHILE (counter <= end) DO
INSERT INTO table1 VALUES (start,null,counter,start+counter,end);
SET counter = counter+1;
END WHILE;
END;
Try it here : https://dbfiddle.uk/X6vP3wKW

MYSQL: how can i replace multiple parts of a string value by comparing it to an entire table

I have table_1 and table_2; the column in table_1 is a string, lets call it s1, with multiple values separated by comma, EXAMPLE: 'tmp,a1,a2,a6,a7' ; the numbers after the 'a' are unique; in table_2 there are two columns: c1, c2; c1 contains all the numbers after the 'a' in s1; c2 cointains the numbers that need to replace the ones in s1, if they are null they dont need to be replaced.
I need to modify all the numbers after the 'a' with the numbers in c2 when they are equal to the numbers in c1 and when c2 is not null
How can i do this in mySQL
i tought of using the replace() function, but i dont know if i can insert a query inside of it
EDIT: i know this violate first rule of integrity, but i have no choice since i was told to not modify existing table
A friend of mine helped me solve this problem: so what it does it take the value, put it in a string, then substring it piece by piece and each time comparing it to the table and then it recompress it into another value.
`CREATE DEFINER=`root`#`%` PROCEDURE `sp_ik_rinomina_campi_extra`(in nome_tabella varchar(200), in nome_colonna varchar(200))
BEGIN
/* dichiarazione variabili */
DECLARE finished INTEGER default 0;
declare valore_colonna varchar(2000);
declare OLD_valore_colonna varchar(2000);
declare NEW_valore_colonna varchar(2000);
declare app_valore varchar(100);
declare cnt integer;
declare old_id integer;
declare new_id integer;
declare cur_cri cursor for
SELECT col1 FROM app_cri;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
/* fine dichiarazione variabili */
select '1';
/* controllo se esiste la tabella temporanea e in caso la cancello */
set #ret = 0;
SELECT count(1) INTO #ret FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'app_cri';
if #ret = 1 then
drop table app_cri;
end if;
set #ret = 0;
SELECT count(1) INTO #ret FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ext_result_mappatura';
if #ret = 1 then
drop table ext_result_mappatura;
end if;
create table if not exists app_cri
(
col1 varchar(4000)
);
create table ext_result_mappatura
(
old_campo varchar(4000) ,
new_campo varchar(4000)
);
TRUNCATE TABLE app_cri;
set #sql = 'insert into app_cri select ';
set #sql = concat(#sql,nome_colonna);
set #sql = concat(#sql,' from ');
set #sql = concat(#sql,nome_tabella);
PREPARE dynamic_statement FROM #sql;
EXECUTE dynamic_statement;
DEALLOCATE PREPARE dynamic_statement;
open cur_cri;
getcur: loop
fetch next from cur_cri into old_valore_colonna;
if finished = 1 then
leave getcur;
end if;
set valore_colonna = concat(old_valore_colonna,','); /*aggiungo una , in fondo */
set new_valore_colonna = ''; /* pulisco il campo new */
while valore_colonna <> '' do
if new_valore_colonna <> '' then
set new_valore_colonna = concat(new_valore_colonna,',');
end if;
set app_valore = (SELECT SUBSTRING_INDEX(valore_colonna, ',', 1));
if substring(app_valore,1,5) = 'Extra' then
set NEW_valore_colonna = concat(new_valore_colonna,'Extra');
set old_id = substring(app_valore,6);
set new_id = 0;
set new_id = (select nuovivalori from TempTable6 where vecchivalori = old_id);
if new_id > 0 then
set new_valore_colonna = concat(new_valore_colonna,new_id);
else
set new_valore_colonna = concat(new_valore_colonna,old_id);
end if;
else
set new_valore_colonna = concat(new_valore_colonna,app_valore);
end if;
set valore_colonna = substring(valore_colonna,LOCATE(',', valore_colonna)+1);
end while;
insert into ext_result_mappatura values (old_valore_colonna,new_valore_colonna);
end loop getcur;
close cur_cri;
END

Can I check a database for existing constraint violations (Getting unique constraint violation on production, cannot reproduce it locally)

In a Magento 2 shop I am getting a unique constraint violation on production which I cannot debug, but reproduce there.
I exported the full live database, imported locally and cannot reproduce it any more there.
My current working theory is, that the production database is inconsistent and exporting + importing fixes the problem.
How can I verify this thesis? Is there a command which can check a current MySQL database for any existing constraint violations?
EDIT: My problem seems to be on Magento level somehow... see https://magento.stackexchange.com/questions/277627/possible-bug-in-and-handling-of-getorigdata-when-updating-tier-prices
Assuming that you verified the query below returns false for the keys you suspected:
SELECT
COUNT(0) <> 0 AS KeyViolated
FROM (
SELECT
NULL
FROM my_schema.my_table
GROUP BY unique_, key, columns
HAVING (COUNT(0) > 1) ) A
;
I know that one can list FK violations using this answer. For verifying key constraints one could use the query from here and regular group by & having count query:
DROP PROCEDURE IF EXISTS sp_validate_keys;
DROP PROCEDURE IF EXISTS statement;
DELIMITER $$
CREATE PROCEDURE statement(IN dynamic_statement TEXT)
BEGIN
SET #dynamic_statement := dynamic_statement;
PREPARE prepared_statement FROM #dynamic_statement;
EXECUTE prepared_statement;
DEALLOCATE PREPARE prepared_statement;
END$$
DELIMITER ;
DELIMITER $$
CREATE PROCEDURE sp_validate_keys()
BEGIN
DECLARE var_cur_idx INTEGER UNSIGNED DEFAULT 1;
DECLARE var_length INTEGER UNSIGNED DEFAULT 0;
DECLARE var_schema_name VARCHAR(64);
DECLARE var_table_name VARCHAR(64);
DECLARE var_column_names VARCHAR(255);
DECLARE var_cur_statement TEXT;
CREATE OR REPLACE VIEW v_db_table_key AS
select stat.table_schema as database_name,
stat.table_name,
-- stat.index_name,
group_concat(stat.column_name
order by stat.seq_in_index separator ', ') as columns
-- , tco.constraint_type
from information_schema.statistics stat
join information_schema.table_constraints tco
on stat.table_schema = tco.table_schema
and stat.table_name = tco.table_name
and stat.index_name = tco.constraint_name
where stat.non_unique = 0
and stat.table_schema not in ('information_schema', 'sys',
'performance_schema', 'mysql')
group by stat.table_schema,
stat.table_name,
stat.index_name,
tco.constraint_type
order by stat.table_schema,
stat.table_name
;
SET var_length := (SELECT COUNT(0) FROM v_db_table_key);
DROP TEMPORARY TABLE IF EXISTS tmp_db_table_key_idx;
CREATE TEMPORARY TABLE tmp_db_table_key_idx AS
SELECT
v.*,
(#cnt := #cnt + 1) AS idx
FROM v_db_table_key v
CROSS JOIN (SELECT #cnt := 0) _
;
DROP TEMPORARY TABLE IF EXISTS tmp_key_validation;
CREATE TEMPORARY TABLE tmp_key_validation AS (SELECT * FROM tmp_db_table_key_idx LIMIT 0);
WHILE (var_cur_idx <= var_length) DO
SET var_schema_name := (
SELECT database_name FROM tmp_db_table_key_idx WHERE idx = var_cur_idx LIMIT 1);
SET var_table_name := (
SELECT table_name FROM tmp_db_table_key_idx WHERE idx = var_cur_idx LIMIT 1);
SET var_column_names := (
SELECT columns FROM tmp_db_table_key_idx WHERE idx = var_cur_idx LIMIT 1);
SET var_cur_statement := CONCAT('
INSERT INTO tmp_key_validation
SELECT
\'', var_schema_name, '\' AS SchemaName,
\'', var_table_name, '\' AS TableName,
\'', var_column_names, '\' AS KeyColumns,
COUNT(0) <> 0 AS KeyViolated
FROM (
SELECT
NULL
FROM ', var_schema_name, '.', var_table_name, '
GROUP BY ', var_column_names, '
HAVING (COUNT(0) > 1)
) A
;
')
;
CALL statement(var_cur_statement);
SET var_cur_idx := var_cur_idx + 1;
END WHILE;
-- SELECT var_cur_statement;
DROP VIEW IF EXISTS v_db_table_key;
SELECT * FROM tmp_key_validation WHERE idx = TRUE;
END $$
DELIMITER ;
CALL sp_validate_keys();
DROP PROCEDURE IF EXISTS sp_validate_keys;
DROP PROCEDURE IF EXISTS statement;

select from unknown number of tables with parameter

I want to select (union) rows from multiple tables using Parameter
I have table w with two columns:
The column table_name is referring to other tables in my DB, and condition is the 'where' that should be added to the query.
table_name | condition
---------------------
x | y=2
x | r=3
t | y=2
the query should be something like:
select * from x where y=2
union
select * from x where r=3
union
select * from t where y=2
of course that the number of unions is unknown.
Should it be stored procedure? cursor?
One way to get this done. Initial answer was SQL Server syntax. This edit has the MySQL syntax. Make sure your temp table cannot be accessed at the same time. E.g. In MySQL temp tables are unique to the connection. Also add your error checking. In MySQL set the appropriate varchar size for your needs. I used 1024 across the board just for testing purposes.
MySQL syntax
CREATE table test (
id int,
table_name varchar(1024),
where_c varchar(1024)
);
INSERT into test(id, table_name, where_c) values
(1,'x','y=2'),
(2,'x','r=3'),
(3,'t','y=2');
DROP PROCEDURE IF EXISTS generate_sql;
DELIMITER //
CREATE PROCEDURE generate_sql()
BEGIN
DECLARE v_table_name VARCHAR(1024);
DECLARE v_where_c VARCHAR(1024);
DECLARE table_id INT;
DECLARE counter INT;
DECLARE v_SQL varchar(1024);
CREATE TEMPORARY table test_copy
SELECT * FROM test;
SET v_SQL = '';
SET counter = (SELECT COUNT(1) FROM test_copy);
WHILE counter > 0 DO
SELECT id, table_name, where_c
INTO table_id, v_table_name, v_where_c
FROM test_copy LIMIT 1;
SET v_SQL = CONCAT(v_SQL, 'SELECT * FROM ', v_table_name, ' WHERE ', v_where_c);
DELETE FROM test_copy WHERE id=table_id;
SET counter = (SELECT COUNT(1) FROM test_copy);
IF counter > 0 THEN
SET v_SQL = CONCAT(v_SQL,' UNION ');
ELSE
SET v_SQL = v_SQL;
END IF;
END WHILE;
DROP table test_copy;
SELECT v_SQL;
END //
DELIMITER ;
call generate_sql()
SQL Server syntax
CREATE table test (
id int,
table_name varchar(MAX),
condition varchar(MAX)
);
INSERT into test(id, table_name, condition) values
(1,'x','y=2'),
(2,'x','r=3'),
(3,'t','y=2');
SELECT * INTO #temp FROM test;
DECLARE #SQL varchar(MAX);
SET #SQL='';
while exists (select * from #temp)
begin
DECLARE #table_name varchar(MAX);
DECLARE #condition varchar(MAX);
DECLARE #table_id int;
SELECT top 1 #table_id=id, #table_name=table_name, #condition=condition FROM #temp;
SET #SQL += 'SELECT * FROM ' + #table_name + ' WHERE ' + #condition;
delete #temp where id = #table_id;
if exists (select * from #temp) SET #SQL += ' UNION ';
end
SELECT #SQL;
drop table #temp;
Assuming that tables x and t have the same definition and that you want to ignore duplicate results by using UNION rather than UNION ALL, the following should work:
SET #sql = '';
SELECT GROUP_CONCAT(
CONCAT('SELECT * FROM `', `table_name`, '` WHERE ', `condition`)
SEPARATOR ' UNION ') INTO #sql
FROM w;
SET #sql = CONCAT('SELECT * FROM ( ', #sql, ' ) a;');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
(I've edited your question slightly because you had two different definitions for table x)

the target table is not updatable (update view)

hello i want to update a view using this procedure but i got the error below:
Procedure:
CREATE DEFINER=`root`#`localhost` PROCEDURE `setijfchauffeur`()
begin
declare cnt int;
declare c int;
declare cn int;
declare nomvol varchar(11);
declare i int;
declare rest int;
/*create a table with ID and ARR_num*/
create table ijfnbrvols select distinct ARR_num from ijforder ;
alter table ijfnbrvols add column ID int primary key auto_increment after ARR_num;
select count(*) into cnt from ijfnbrvols ;
set c = 1;
while (c<= cnt) do
select ARR_num into nomvol from ijfnbrvols where ID = c;
select count(*) into cn from ijforder where ARR_num = nomvol;
if (cn <= 3) and (cnt !=0) THEN
update ijforder set Chauffeur ='berline' where ARR_num = nomvol;
END IF;
if (cnt <= 8) and (cnt >=4) THEN
update ijforder set Chauffeur ='minibus' where ARR_num = nomvol;
END IF;
set c= c+1;
End while;
END
i got this error: Error Code: 1288. The target table ijforder of the UPDATE is not updatable
Help please :) thanks