Подсчет нулей для каждого столбца в таблице

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

В первой части процедуры получаются все имена столбцов. Это работает, так как dbms_output правильно перечисляет их все.

Во-вторых, запрос вставляет количество нулевых значений в переменную 'nullscount'. Эта часть не работает, так как вывод для этой переменной всегда равен 0, даже для столбцов, где мы знаем, что есть нули.

Кто-нибудь знает, как правильно обрабатывать вторую часть?

Большое спасибо.

CREATE OR REPLACE PROCEDURE COUNTNULLS AS 
nullscount int;

BEGIN

for c in (select column_name from all_tab_columns where table_name = upper('gp'))
loop

select count(*) into nullscount from gp where c.column_name is null;

dbms_output.put_line(c.column_name||' '||nullscount);
end loop;
 
END COUNTNULLS;
c.column_name — это просто строка, и она не равна нулю. Вам нужен динамический SQL.
PM 77-1 14.09.2022 18:46

Какая у вас версия оракула?

Sayan Malakshinov 14.09.2022 20:23
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
2
70
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

c.column_name никогда не бывает нулевым, потому что это содержимое столбца "column_name" таблицы "all_tab_columns" не столбец, имя которого является значением c.column_name, в таблице gp. Вы должны использовать динамический запрос и ВЫПОЛНИТЬ НЕМЕДЛЕННО, чтобы достичь того, чего вы хотите.

Это действительно была проблема, замена на динамический запрос решила проблему.

Clementine Sin 15.09.2022 09:27

Вы можете получить его с помощью всего одного запроса: этот запрос сканирует таблицу только один раз:

DBFiddle: https://dbfiddle.uk/asgrCezT

select *
from xmltable(
        '/ROWSET/ROW/*'
        passing
        dbms_xmlgen.getxmltype(
            (
            select 
                'select '
              ||listagg('count(*)-count("'||column_name||'") as "'||column_name||'"',',')
              ||' from '||upper('gp')
            from user_tab_columns 
            where table_name = upper('gp')
            )
        )
        columns
            column_name varchar2(30) path './name()',
            cnt_nulls   int path '.'
        );

Результаты:

COLUMN_NAME                     CNT_NULLS
------------------------------ ----------
A                                       5
B                                       4
C                                       3

Динамический sql в этом запросе использует (24 символа + длина имени столбца), поэтому он должен нормально работать, например, для 117 столбцов со средней длиной имени столбца = 10. Если вам нужно больше, вы можете немного переписать его, например:

select *
from xmltable(
        'let $cnt := /ROWSET/ROW/CNT
         for $r in /ROWSET/ROW/*[name() != "CNT"]
           return <R name = "{$r/name()}"> {$cnt - $r} </R>'
        passing
        dbms_xmlgen.getxmltype(
            (
            select 
                'select count(*) CNT,'
              ||listagg('count("'||column_name||'") as "'||column_name||'"',',')
              ||' from '||upper('gp')
            from user_tab_columns 
            where table_name = upper('gp')
            )
        )
        columns
            column_name varchar2(30) path '@name',
            cnt_nulls   int path '.'
        );

Я подтверждаю, что это работает, как сказал Саян, в одном запросе без необходимости использования PLSQL.

Clementine Sin 15.09.2022 09:36

Это также может быть принятым ответом, но, поскольку этот пост уже получил одобрение, другой выбран для равного распределения одинаково полезных ответов - приносим извинения за это и большое спасибо за вашу помощь.

Clementine Sin 15.09.2022 09:42

@ClementineSin обратите внимание, что это решение не требует чтения таблицы столько раз, сколько у вас есть столбцов. Это решение сканирует вашу таблицу только один раз.

Sayan Malakshinov 15.09.2022 13:09
Ответ принят как подходящий
create table gp (
    id                             number generated by default on null as identity 
                                   constraint gp_pk primary key,
    c1                             number,
    c2                             number,
    c3                             number,
    c4                             number,
    c5                             number
)
;

-- add some data with NULLS and numbers
DECLARE
BEGIN
  FOR r IN 1 .. 20 LOOP
    INSERT INTO gp (c1,c2,c3,c4,c5) VALUES 
    (CASE WHEN mod(r,2) = 0 THEN NULL ELSE mod(r,2) END
    ,CASE WHEN mod(r,3) = 0 THEN NULL ELSE mod(r,3) END
    ,CASE WHEN mod(r,4) = 0 THEN NULL ELSE mod(r,4) END
    ,CASE WHEN mod(r,5) = 0 THEN NULL ELSE mod(r,5) END
    ,5);
  END LOOP;
END;
/

-- check what is in the table
SELECT * FROM gp;

-- do count of each column
DECLARE
  l_colcount NUMBER;
  l_statement VARCHAR2(100) := 'SELECT COUNT(*) FROM $TABLE_NAME$ WHERE $COLUMN_NAME$ IS NULL';
BEGIN
  FOR r IN (SELECT column_name,table_name FROM user_tab_columns WHERE table_name = 'GP') LOOP
  
    EXECUTE IMMEDIATE REPLACE(REPLACE(l_statement,'$TABLE_NAME$',r.table_name),'$COLUMN_NAME$',r.column_name) INTO l_colcount;
    
    dbms_output.put_line('Table: '||r.table_name||', column'||r.column_name||', COUNT: '||l_colcount);
  END LOOP;
END;
/

Table created.

Statement processed.

Result Set 4
ID  C1  C2  C3  C4  C5
1   1   1   1   1   5
2    -  2   2   2   5
3   1    -  3   3   5
4    -  1    -  4   5
5   1   2   1    -  5
6    -   -  2   1   5
7   1   1   3   2   5
8    -  2    -  3   5
9   1    -  1   4   5
10   -  1   2    -  5
11  1   2   3   1   5
12   -   -   -  2   5
13  1   1   1   3   5
14   -  2   2   4   5
15  1    -  3    -  5
16   -  1    -  1   5
17  1   2   1   2   5
18   -   -  2   3   5
19  1   1   3   4   5
20   -  2    -   -  5

20 rows selected.

Statement processed.

Table: GP, columnID, COUNT: 0
Table: GP, columnC1, COUNT: 10
Table: GP, columnC2, COUNT: 6
Table: GP, columnC3, COUNT: 5
Table: GP, columnC4, COUNT: 4
Table: GP, columnC5, COUNT: 0

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

Clementine Sin 15.09.2022 09:26

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

Похожие вопросы