Мы хотим подсчитать, сколько нулей имеет каждый столбец в таблице. Слишком много столбцов, чтобы делать это по одному, поэтому была создана следующая процедура 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 никогда не бывает нулевым, потому что это содержимое столбца "column_name" таблицы "all_tab_columns" не столбец, имя которого является значением c.column_name, в таблице gp. Вы должны использовать динамический запрос и ВЫПОЛНИТЬ НЕМЕДЛЕННО, чтобы достичь того, чего вы хотите.
Это действительно была проблема, замена на динамический запрос решила проблему.
Вы можете получить его с помощью всего одного запроса: этот запрос сканирует таблицу только один раз:
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.
Это также может быть принятым ответом, но, поскольку этот пост уже получил одобрение, другой выбран для равного распределения одинаково полезных ответов - приносим извинения за это и большое спасибо за вашу помощь.
@ClementineSin обратите внимание, что это решение не требует чтения таблицы столько раз, сколько у вас есть столбцов. Это решение сканирует вашу таблицу только один раз.
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
Я проверил это, и это сработало для этой проблемы, разница с запросом в вопросе действительно заключалась в том, что, как указано ниже, необходимо было использовать динамический запрос.
c.column_name— это просто строка, и она не равна нулю. Вам нужен динамический SQL.