DGMS89
DGMS89

Reputation: 1677

Prepared statement to loop rows MySQL

Scenario: Following up from my previous question (Using a cursor in a stored procedure to loop rows MySQL) I am trying to do a nested prepare statement in which I input a date to the outer one, and that calls the inner, which gets data from a table.

Code:

-- Create temporary table for the Output:
drop temporary table if exists `stats`;
create temporary table `stats`
(
    col_name varchar(32) null,
    num_nulls int null,
    num_values int null
);


-- Procedure for the check:
drop procedure if exists `set_column_null_stats`;
delimiter $$
create procedure `set_column_null_stats`
(`p_col_name` varchar(128), `wanted_date` date)
begin

-- Set variables:
set @col_nme = `p_col_name`;
set @date1 = `wanted_date`;

prepare stmt from 'insert into `stats` (`col_name`) values (?);';
execute stmt using @col_nme;
deallocate prepare stmt;

-- count number of NULLS based on conditions:
set @sql_txt = concat(
            'update `stats` s join(
            select 
            count(1) as `nb`
            from `btc`
            where`btc`.`date` = ', @date1, ' and `btc`.`', @col_nme, '` is null)
            t set `num_nulls` = t.`nb` where `col_name` = \'', @col_nme, '\';');
prepare stmt from @sql_txt;
execute stmt;
deallocate prepare stmt;

-- count number of not NULLS based on conditions:
set @sql_txt = concat(
           'update `stats` s join(
            select
            count(1) as `nb`
            from `btc`
            where `btc`.`date` = ', @date1, ' and `btc`.`', @col_nme, '` is not null)
            -- t set `num_values` = t.`nb` where `col_name` = \'', @col_nme, '\';');        
set @sql_txt = concat('update `stats` s join (select count(1) as `nb` from `btc` where `', @col_nme, '` is not null) t set `num_values` = t.`nb` where `col_name` = \'', @col_nme, '\';');
prepare stmt from @sql_txt;
execute stmt;
deallocate prepare stmt;

end$$
delimiter ;


-- Procedure for looping through rows of `wanted_columns` table:
delimiter $$
drop procedure if exists `data_check_loop` $$
create procedure `data_check_loop`(`wanted_date` date)
begin

declare dateval date default null;
declare colval text default null;

-- boolean variable to indicate cursor is out of data
declare done tinyint default false;

-- declare a cursor to select the desired columns from the desired source table
declare cursor1
    cursor for
        select *
        from `wanted_columns`; 

-- catch exceptions
        declare continue handler for not found set done = true;

set dateval = `wanted_date`;

-- open the cursor
        open cursor1;
            my_loop: 
            loop
                fetch next from cursor1 into colval;
                if done then 
                    leave my_loop; 
                else  
                    call `set_column_null_stats`(colval, dateval);
                end if;
            end loop;
        close cursor1;

end $$
delimiter ;


-- Start the process with the wanted date:
call `data_check_loop`('2018-08-13');

select * from `stats`;

Issue: This code runs with no errors, but does not give me any result. If I run just the first prepared statement, feeding the variables directly one by one, it works fine. So I am guessing the problem is with my second statement.

Question: Any ideas on what I am doing wrong here?

Obs: The second code should loop rows from a table (wanted columns) and feed them to the first statement, one by one (alongside with the date, which should always be the same)

Obs2: My objective with this query is: from a table with a list of names as rows ("id1", "date1"...) I intend to read each row and use that value into another table where the names ("id1", "date1"...) are the columns, and get a sum for each of my wanted columns, of the NULL and not NULL values (also, given another constraint of the date input). Finally, for each of my original rows (table1) I will output a new row with #NULL and #notNULL.

Ex. Table 1:

Col_names
  Id1
  Name1
  Date1
  Process
  Time
  Class

Ex. Table 2:

Id1    Name1    Date1    Process    Time    Class
aa     test1    01/01       3       NULL      A
NULL   test2    01/02       4       NULL      b
bb     test3    NULL        3       NULL     NULL

Ex. Output:

Col_name    #Null    #notNull
  Id1         1          2
  Name1       0          3
  Date1       1          2 
  Process     0          3
  Time        3          0
  Class       1          2

Upvotes: 1

Views: 627

Answers (1)

Raymond Nijland
Raymond Nijland

Reputation: 11602

It's seams you want to convert columns into records end count where the values are null or not null for that column .
The process which converts columns into records is called unpivot in most database systems this is supported with UNPIVOT() but MySQL does not support this.
So normally this is done with UNION in combination with some aggregate functions like MAX(), MIX(), SUM() and COUNT() and a CASE END clause to simulate UNPIVOT() on MySQL.

Query

SELECT 
   'Id1' AS Col_name
 , SUM(CASE WHEN Table1.Id1 IS NULL THEN 1 ELSE 0 END) AS `#Null`
 , SUM(CASE WHEN Table1.Id1 IS NOT NULL THEN 1 ELSE 0 END) AS `#notNull` 
FROM 
 Table1

UNION ALL

SELECT 
  'Name1' AS Col_name
 , SUM(CASE WHEN Table1.Name1 IS NULL THEN 1 ELSE 0 END) AS `#Null`
 , SUM(CASE WHEN Table1.Name1 IS NOT NULL THEN 1 ELSE 0 END) AS `#notNull` 
FROM 
 Table1

UNION ALL

SELECT 
  'Date1' AS Col_name
 , SUM(CASE WHEN Table1.Date1 IS NULL THEN 1 ELSE 0 END) AS `#Null`
 , SUM(CASE WHEN Table1.Date1 IS NOT NULL THEN 1 ELSE 0 END) AS `#notNull` 
FROM 
 Table1

UNION ALL

SELECT 
  'Process' AS Col_name
 , SUM(CASE WHEN Table1.Process IS NULL THEN 1 ELSE 0 END) AS `#Null`
 , SUM(CASE WHEN Table1.Process IS NOT NULL THEN 1 ELSE 0 END) AS `#notNull` 
FROM 
 Table1

UNION ALL

SELECT 
  'Time' AS Col_name
 , SUM(CASE WHEN Table1.Time IS NULL THEN 1 ELSE 0 END) AS `#Null`
 , SUM(CASE WHEN Table1.Time IS NOT NULL THEN 1 ELSE 0 END) AS `#notNull` 
FROM 
 Table1

UNION ALL

SELECT 
  'Class' AS Col_name
 , SUM(CASE WHEN Table1.Class IS NULL THEN 1 ELSE 0 END) AS `#Null`
 , SUM(CASE WHEN Table1.Class IS NOT NULL THEN 1 ELSE 0 END) AS `#notNull` 
FROM 
 Table1

Results

| Col_name | #Null | #notNull |
| -------- | ----- | -------- |
| Id1      | 1     | 2        |
| Name1    | 0     | 3        |
| Date1    | 1     | 2        |
| Process  | 0     | 3        |
| Time     | 3     | 0        |
| Class    | 1     | 2        |

demo

Upvotes: 1

Related Questions