31

I was wondering about the possibility to count the null columns of row in SQL, I have a table Customer that has nullable values, simply I want a query that return an int of the number of null columns for certain row(certain customer).

Islam
  • 1,647
  • 6
  • 27
  • 40
  • That is not what you should want to do in sql – zerkms Dec 21 '11 at 21:19
  • For my own curiosity, why would you want to know this? – UnhandledExcepSean Dec 21 '11 at 21:23
  • What RDBMS? What are the columns? Are they all of the same datatype? – Martin Smith Dec 21 '11 at 21:23
  • you mean that any solution for that case would be ugly solution? – Islam Dec 21 '11 at 21:24
  • 1
    @SpectralGhost The case is the that I want to construct a progress bar for the customer to fill his info, so I need to get the total number of columns and the number of columns which is null so I can get the percentage of the filled data. – Islam Dec 21 '11 at 21:34
  • Ah, an interesting and logical reason for wanting to do this. +1 – UnhandledExcepSean Dec 21 '11 at 21:41
  • @zerkms - Why not? You want to know how well you know that SQL row. – andowero Dec 30 '22 at 12:58
  • @andowero if you read all the comments and the checked answer you will see that what OP asked and what they _really_ need is 2 different tasks: my comment original meaning is "if you need this - then you probably need to rephrase your problem so that you didn't need it", and it's the case there. – zerkms Jan 02 '23 at 03:20

14 Answers14

44

This method assigns a 1 or 0 for null columns, and adds them all together. Hopefully you don't have too many nullable columns to add up here...

SELECT 
  ((CASE WHEN col1 IS NULL THEN 1 ELSE 0 END)
  + (CASE WHEN col2 IS NULL THEN 1 ELSE 0 END)
  + (CASE WHEN col3 IS NULL THEN 1 ELSE 0 END)
  ...
  ...
  + (CASE WHEN col10 IS NULL THEN 1 ELSE 0 END)) AS sum_of_nulls
FROM table
WHERE Customer=some_cust_id

Note, you can also do this perhaps a little more syntactically cleanly with IF() if your RDBMS supports it.

SELECT 
  (IF(col1 IS NULL, 1, 0)
  + IF(col2 IS NULL, 1, 0)
  + IF(col3 IS NULL, 1, 0)
  ...
  ...
  + IF(col10 IS NULL, 1, 0)) AS sum_of_nulls
FROM table
WHERE Customer=some_cust_id

I tested this pattern against a table and it appears to work properly.

Michael Berkowski
  • 267,341
  • 46
  • 444
  • 390
  • `IF()` may well look syntactically cleaner, but `CASE` is more universally supported. Still, to continue this line, from the most universal solution to the cleanest one, I would suggest the following: `SELECT ((col1 IS NULL) + (col2 IS NULL) + ...) AS sum_of_nulls FROM ...`. – Andriy M Jan 01 '12 at 13:02
  • 1
    @ImanMarashi I am unsure what you are asking. The result of this query is a single integer column representing the count of NULL columns per row. – Michael Berkowski Jul 10 '15 at 10:44
5

My answer builds on Michael Berkowski's answer, but to avoid having to type out hundreds of column names, what I did was this:

Step 1: Get a list of all of the columns in your table

SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'myTable';

Step 2: Paste the list in Notepad++ (any editor that supports regular expression replacement will work). Then use this replacement pattern

  • Search:

    ^(.*)$
    
  • Replace:

    \(CASE WHEN \1 IS NULL THEN 1 ELSE 0 END\) +
    

Step 3: Prepend SELECT identityColumnName, and change the very last + to AS NullCount FROM myTable and optionally add an ORDER BY...

SELECT 
    identityColumnName, 
    (CASE WHEN column001 IS NULL THEN 1 ELSE 0 END) +
    -- ...
    (CASE WHEN column200 IS NULL THEN 1 ELSE 0 END) AS NullCount
FROM
    myTable
ORDER BY 
    NullCount DESC
Drew Chapin
  • 7,779
  • 5
  • 58
  • 84
  • You could also make use of sys.columns or something similar to concatenate all of those without something like Notepad. – mateoc15 Feb 10 '21 at 16:34
1

For ORACLE-DBMS only.

You can use the NVL2 function:

NVL2( string1, value_if_not_null, value_if_null )

Here is a select with a similiar approach as Michael Berkowski suggested:

SELECT (NVL2(col1, 0, 1) 
        + NVL2(col2, 0, 1)
        + NVL2(col3, 0, 1)
        ...
        ...
        + NVL2(col10, 0, 1)
        ) AS sum_of_nulls
FROM table
WHERE Customer=some_cust_id

A more generic approach would be to write a PL/SQL-block and use dynamic SQL. You have to build a SELECT string with the NVL2 method from above for every column in the all_tab_columns of a specific table.

Stephan Schielke
  • 2,744
  • 7
  • 34
  • 40
0

Depending on what you want to do, and if you ignore mavens, and if you use SQL Server 2012, you could to it another way. .

The total number of candidate columns ("slots") must be known. 1. Select all the known "slots" column by column (they're known). 2. Unpivot that result to get a table with one row per original column. This works because the null columns don't unpivot, and you know all the column names. 3. Count(*) the result to get the number of non-nulls; subtract from that to get your answer.

Like this, for 4 "seats" in a car

select 'empty seats' = 4 - count(*)
from 
(
    select carId, seat1,seat2,seat3,seat4 from cars where carId = @carId
) carSpec
unpivot (FieldValue FOR seat in ([seat1],[seat2],[seat3],[seat4])) AS results

This is useful if you may need to do more later than just count the number of non-null columns, as it gives you a way to manipulate the columns as a set too.

philw
  • 661
  • 10
  • 20
0

This will give you the number of columns which are not null. you can apply this appropriately

SELECT   ISNULL(COUNT(col1),'') + ISNULL(COUNT(col2),'') +ISNULL(COUNT(col3),'') 
   FROM TABLENAME
   WHERE  ID=1
Josh B
  • 1,748
  • 16
  • 19
  • Strictly speaking, the `ISNULL()` isn't required, as `COUNT(*)` returns a value. Note that `ISNULL()` isn't present on every system, and the OP never specified what RDBMS he was using, meaning the answer might not have worked for him either. And finally... if `COUNT()` somehow _did_ return a null, the fact that you're returning a **blank** is going to cause this to fail; either you get a character string instead of the total, or the DB complains about type mismatches. For that, and the fact the question is nearly 2 years old with an accepted answer, -1 (basic idea works though) – Clockwork-Muse Oct 18 '13 at 09:08
0

The below script gives you the NULL value count within a row i.e. how many columns do not have values.

{SELECT
    *,
    (SELECT COUNT(*)
    FROM (VALUES (Tab.Col1)
                ,(Tab.Col2)
                ,(Tab.Col3)
                ,(Tab.Col4)) InnerTab(Col) 
        WHERE Col IS NULL) NullColumnCount
FROM (VALUES(1,2,3,4)
            ,(NULL,2,NULL,4)
            ,(1,NULL,NULL,NULL)) Tab(Col1,Col2,Col3,Col4) } 

Just to demonstrate I am using an inline table in my example.

Try to cast or convert all column values to a common type it will help you to compare the column of different type.

  • I am not familiar with "InnerTab". I googled it and can not find a definition. Could someone provide a clue for me as to what this means? – glenn garson Aug 15 '17 at 16:06
0

I haven't tested it yet, but I'd try to do it using a PL\SQL function

CREATE OR REPLACE TYPE ANYARRAY AS TABLE OF ANYDATA
;

CREATE OR REPLACE Function COUNT_NULL
( ARR IN ANYARRAY )
RETURN number
IS
   cnumber number ;
BEGIN

   for i in 1 .. ARR.count loop
     if ARR(i).column_value is null then
       cnumber := cnumber + 1;
     end if;
   end loop;

RETURN cnumber;

EXCEPTION
WHEN OTHERS THEN
   raise_application_error
   (-20001,'An error was encountered - '
   ||SQLCODE||' -ERROR- '||SQLERRM);
END
;

Then use it in a select query like this

CREATE TABLE TEST (A NUMBER, B NUMBER, C NUMBER);

INSERT INTO TEST (NULL,NULL,NULL);
INSERT INTO TEST (1   ,NULL,NULL);
INSERT INTO TEST (1   ,2   ,NULL);
INSERT INTO TEST (1   ,2   ,3   );

SELECT ROWNUM,COUNT_NULL(A,B,C) AS NULL_COUNT FROM TEST;

Expected output

ROWNUM | NULL_COUNT
-------+-----------
     1 | 3
     2 | 2
     3 | 1
     4 | 0
Khaled.K
  • 5,828
  • 1
  • 33
  • 51
0

This is how i tried

CREATE TABLE #temptablelocal (id int NOT NULL, column1 varchar(10) NULL, column2 varchar(10) NULL, column3 varchar(10) NULL, column4 varchar(10) NULL, column5 varchar(10) NULL, column6 varchar(10) NULL);


INSERT INTO #temptablelocal
VALUES (1,
        NULL,
        'a',
        NULL,
        'b',
        NULL,
        'c')
SELECT *
FROM #temptablelocal
WHERE id =1
  SELECT count(1) countnull
  FROM
    (SELECT a.ID,
            b.column_title,
            column_val = CASE b.column_title    
            WHEN 'column1' THEN a.column1 
            WHEN 'column2' THEN a.column2 
            WHEN 'column3' THEN a.column3 
            WHEN 'column4' THEN a.column4 
            WHEN 'column5' THEN a.column5 
            WHEN 'column6' THEN a.column6 
            END
     FROM
       ( SELECT id,
                column1,
                column2,
                column3,
                column4,
                column5,
                column6
        FROM #temptablelocal
        WHERE id =1 ) a
     CROSS JOIN
       ( SELECT 'column1'
        UNION ALL SELECT 'column2'
        UNION ALL SELECT 'column3'
        UNION ALL SELECT 'column4'
        UNION ALL SELECT 'column5'
        UNION ALL SELECT 'column6' ) b (column_title) ) AS pop WHERE column_val IS NULL
  DROP TABLE #temptablelocal

enter image description here

Mayank
  • 1,351
  • 5
  • 23
  • 42
0

Similary, but dynamically:

drop table if exists myschema.table_with_nulls;

create table myschema.table_with_nulls as

select 
   n1::integer,
   n2::integer,
   n3::integer,
   n4::integer,
   c1::character varying,
   c2::character varying,
   c3::character varying,
   c4::character varying
from 
   (
      values 
         (1,2,3,4,'a','b','c','d'),
         (1,2,3,null,'a','b','c',null),
         (1,2,null,null,'a','b',null,null),
         (1,null,null,null,'a',null,null,null)
   ) as test_records(n1, n2, n3, n4, c1, c2, c3, c4);


drop function if exists myschema.count_nulls(varchar,varchar);

create function myschema.count_nulls(schemaname varchar, tablename varchar) returns void as 
$BODY$
   declare
      calc varchar;
      sqlstring varchar;
begin
   select
      array_to_string(array_agg('(' || trim(column_name) || ' is null)::integer'),' + ') 
   into
      calc
   from
      information_schema.columns
   where 
      table_schema in ('myschema') 
         and table_name in ('table_with_nulls'); 

   sqlstring = 'create temp view count_nulls as select *, ' || calc || '::integer as count_nulls from myschema.table_with_nulls';

   execute sqlstring;

   return;
end;
$BODY$ LANGUAGE plpgsql STRICT;

select * from myschema.count_nulls('myschema'::varchar,'table_with_nulls'::varchar);


select
   *
from 
   count_nulls;

Though I see that I didn't finish parametising the function.

Mark Andrew
  • 166
  • 1
  • 7
0

My answer builds on Drew Chapin's answer, but with changes to get the result using a single script:

use <add_database_here>;
Declare @val Varchar(MAX); 

Select @val = COALESCE(@val + str, str) From 
    (SELECT
    '(CASE WHEN '+COLUMN_NAME+' IS NULL THEN 1 ELSE 0 END) +' str
    FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '<add table name here>'
    ) t1 -- getting column names and adding the case when to replace NULLs for zeros or ones
    
Select @val = SUBSTRING(@val,1,LEN(@val) - 1) -- removing trailling add sign
    
Select @val = 'SELECT <add_identity_column_here>, ' + @val + ' AS NullCount FROM <add table name here>' -- adding the 'select' for the column identity, the 'alias' for the null count column, and the 'from'

EXEC (@val) --executing the resulting sql
Pedro Pinheiro
  • 1,059
  • 14
  • 32
0

With ORACLE:

Number_of_columns - json_value( json_array( comma separated list of columns ), '$.size()' ) from your_table  

json_array will build an array with only the non null columns and the json_query expression will give you the size of the array

p3consulting
  • 2,721
  • 2
  • 12
  • 10
0

Unfortunately, in a standard SQL statement you will have to enter each column you want to test, to test all programatically you could use T-SQL. A word of warning though, ensure you are working with genuine NULLS, you can have blank stored values that the database will not recognise as a true NULL (I know this sounds strange).

You can avoid this by capturing the blank values and the NULLS in a statement like this:

CASE WHEN col1 & '' = '' THEN 1 ELSE 0 END

Or in some databases such as Oracle (not sure if there are any others) you would use:

CASE WHEN col1 || '' = '' THEN 1 ELSE 0 END
Matt Donnan
  • 4,933
  • 3
  • 20
  • 32
0

You don't state RDBMS. For SQL Server 2008...

SELECT CustomerId,
       (SELECT COUNT(*) - COUNT(C)
        FROM   (VALUES(CAST(Col1 AS SQL_VARIANT)),
                      (Col2),
                      /*....*/
                      (Col9),
                      (Col10)) T(C)) AS NumberOfNulls
FROM   Customer  
Martin Smith
  • 438,706
  • 87
  • 741
  • 845
-1

There isn't a straightforward way of doing so like there would be with counting rows. Basically, you have to enumerate all the columns that might be null in one expression.

So for a table with possibly null columns a, b, c, you could do this:

SELECT key_column, COALESCE(a,0) + COALESCE(b,0) + COALESCE(c,0) null_col_count
  FROM my_table
Luis Casillas
  • 29,802
  • 7
  • 49
  • 102
  • This won't work. If a,b,c aren't numeric it will raise an error. If they are numeric it will return the wrong results. – Martin Smith Dec 21 '11 at 21:28