Подготовленный оператор для цикла строк MySQL

Сценарий: Следуя моему предыдущему вопросу (Использование курсора в хранимой процедуре для зацикливания строк MySQL), я пытаюсь выполнить вложенный оператор подготовки, в котором я ввожу дату во внешнюю, а это вызывает внутреннюю, которая получает данные из таблицы.

Код:

-- 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`;

Проблема: Этот код работает без ошибок, но не дает мне никакого результата. Если я запускаю только первый подготовленный оператор, вводя переменные напрямую одну за другой, он работает нормально. Итак, я предполагаю, что проблема в моем втором утверждении.

Вопрос: Есть идеи, что я здесь делаю не так?

Наблюдения: Второй код должен зацикливать строки из таблицы (требуемые столбцы) и передавать их первому оператору, одну за другой (вместе с датой, которая всегда должна быть одинаковой).

Obs2: Моя цель с этим запросом: из таблицы со списком имен в виде строк («id1», «date1» ...) я намерен прочитать каждую строку и использовать это значение в другой таблице, где имена («id1» , "date1" ...) являются столбцами и получают сумму для каждого из моих требуемых столбцов значений NULL, а не NULL (также с учетом другого ограничения ввода даты). Наконец, для каждой из моих исходных строк (table1) я выведу новую строку с #NULL и #notNULL.

Бывший. Таблица 1:

Col_names
  Id1
  Name1
  Date1
  Process
  Time
  Class

Бывший. Таблица 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

Бывший. Вывод:

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

Вы знаете, что открываете себя для возможных (слепых) атак векторов SQL-инъекций в подготовленных запросах UPDATES, верно?

Raymond Nijland 27.11.2018 14:19

@RaymondNijland Понятия не имею, я недавно начал с SQL и пытаюсь собрать воедино все, что нахожу в Интернете. Я даже не знаю, что означают уколы.

DGMS89 27.11.2018 14:21

Первый PREPARE (вставка) защищен, потому что вы используете execute stmt using @col_nme, вы должны сделать тот же подход для обновлений.

Raymond Nijland 27.11.2018 14:23

Возможно, здесь вам даже не понадобится использовать курсор или динамический SQL. Что было бы плохого в том, чтобы сделать обычное обновление?

Tim Biegeleisen 27.11.2018 14:23

@RaymondNijland Хорошо, я постараюсь прочитать об этом и постараюсь исправить.

DGMS89 27.11.2018 14:24

@TimBiegeleisen С уважением, IDK. Я только что нашел другие вопросы, в которых курсоры использовались для зацикливания строк таблицы, и это то, что я пытался сделать. Не знаю, есть ли способ лучше.

DGMS89 27.11.2018 14:25

Можете ли вы предоставить примеры данных и ожидаемых результатов? Я и @TimBiegeleisen уверены, что есть более простой подход к этому .. помощь в предоставлении данных

Raymond Nijland 27.11.2018 14:27

@RaymondNijland Обновлено.

DGMS89 27.11.2018 14:38
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
В последние годы архитектура микросервисов приобрела популярность как способ построения масштабируемых и гибких приложений. Laravel , популярный PHP...
Как построить CRUD-приложение в Laravel
Как построить CRUD-приложение в Laravel
Laravel - это популярный PHP-фреймворк, который позволяет быстро и легко создавать веб-приложения. Одной из наиболее распространенных задач в...
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
В предыдущем посте мы создали функциональность вставки и чтения для нашей динамической СУБД. В этом посте мы собираемся реализовать функции обновления...
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
Роли и разрешения пользователей без пакета Laravel 9
Роли и разрешения пользователей без пакета Laravel 9
Этот пост изначально был опубликован на techsolutionstuff.com .
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
1
8
360
1

Ответы 1

Это швы, которые вы хотите преобразовать столбцы в конечный счетчик записей, где значения для этого столбца - null или not null .
Процесс преобразования столбцов в записи в большинстве систем баз данных называется unpivot. Он поддерживается UNPIVOT(), но MySQL не поддерживает этого.
Обычно это делается с помощью UNION в сочетании с некоторыми агрегатными функциями, такими как MAX(), MIX(), SUM() и COUNT(), и предложением CASE END для имитации UNPIVOT() в MySQL.

Запрос

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

Результаты

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

демонстрация

Спасибо за ответ. На самом деле, я запускал эту процедуру UNION ALL, но поскольку у меня очень длинный список столбцов, и они постоянно меняются, я начал пробовать автоматическую процедуру для их чтения, вместо того, чтобы вводить один за другим вручную несколько раз в день.

DGMS89 27.11.2018 15:04

Нет проблем @ DGMS89, этот db-fiddle.com/f/fDzZWbxq9NX2e7h7YUnA7U/6 - это динамический метод unpivot, который генерирует SQL-запрос на основе количества столбцов. Я сделал шаги видимыми, чтобы было легче следить за происходящим.

Raymond Nijland 27.11.2018 15:23

Огромное спасибо. Я попытаюсь добавить ограничение даты и запустить его.

DGMS89 27.11.2018 15:25

Один вопрос: в запросе я вижу, что вы оставили место для передачи моей "table1", в которой есть данные (в виде строк) для имен столбцов. Я просто не понимаю, как подключиться к таблице, где я получу значения #null и #notnull.

DGMS89 27.11.2018 15:50

Другие вопросы по теме