简体   繁体   中英

Find the percentage of Non-Null rows in SQL

Oh SQL, why do you frustrate me...

Good afternoon everyone, I'm stuck....again....

I have a DB that holds the information for a form (duh), this form can be "Saved" so that the user can come back and work on it later. What I need, is to find the percentage of fields that are NOT NULL. For instance;

COL_A |COL_B |COL_C |ETC.. z  %  
-------------------------- z------
  X   |   X  | NULL |      z  66 
  X   |   X  |   X  |      z  100
 NULL | NULL |   X  |      z  33
 NULL | NULL | NULL |      z  0

The icing on the cake? There are 47 columns that need to be checked for each row. I may be going about this the wrong way. Any help/tips would be appreciated.

You can use CROSS APPLY combined with VALUES :

SELECT t.*, FLOOR(100 * (1.0 * sub.col)/sub.counter) AS [percentage]
FROM #tab t
CROSS APPLY (
   SELECT COUNT(c), COUNT(*) 
   FROM (VALUES(t.col1), (t.col2), (t.col3), (t.col4), (t.col5),
               (t.col6), (t.col7), (t.col8), (t.col9), (t.col10)) AS s(c)
 ) AS sub(col, counter)

You need to add cols up to 47 in VALUES clause.

LiveDemo

EDIT:

The solution above works when columns have similiar datatype or there exists implicit conversion. If there are datatypes where there is no implicit conversion it will fail (like UNIQUEIDENTIFIER and DATETIME . One quick workaround is to wrap every column with CAST(t.colx AS NVARCHAR(100)) :

SELECT t.*, FLOOR(100 * (1.0 * sub.col)/sub.counter) AS [percentage]
FROM #tab t
CROSS APPLY (SELECT COUNT(c), COUNT(*) 
             FROM (VALUES 
             (CAST(t.col1 AS NVARCHAR(MAX))), 
             (CAST(t.col2 AS NVARCHAR(MAX))), 
             (CAST(t.col3 AS NVARCHAR(MAX))), 
             (CAST(t.col4 AS NVARCHAR(MAX))), 
             (CAST(t.col5 AS NVARCHAR(MAX))), 
             (CAST(t.col6 AS NVARCHAR(MAX))), 
             (CAST(t.col7 AS NVARCHAR(MAX))), 
             (CAST(t.col8 AS NVARCHAR(MAX))), 
             (CAST(t.col9 AS NVARCHAR(MAX))), 
             (CAST(t.col10 AS NVARCHAR(MAX)))
             ) AS s(c)) AS sub(col, counter)

LiveDemo2

Alternatively IIF/CASE can be used (no more concerns about datatypes):

SELECT t.*, FLOOR(100 * (1.0 * sub.col)/sub.counter) AS [percentage]
FROM #tab t
CROSS APPLY (SELECT COUNT(c), COUNT(*) 
             FROM (VALUES 
             (IIF(t.col1 IS NULL, NULL, 1)), 
             (IIF(t.col2 IS NULL, NULL, 1)),
             (IIF(t.col3 IS NULL, NULL, 1)), 
             (IIF(t.col4 IS NULL, NULL, 1)),
             (IIF(t.col5 IS NULL, NULL, 1)), 
             (IIF(t.col6 IS NULL, NULL, 1)),
             (IIF(t.col7 IS NULL, NULL, 1)), 
             (IIF(t.col8 IS NULL, NULL, 1)),
             (IIF(t.col9 IS NULL, NULL, 1)), 
             (IIF(t.col10 IS NULL, NULL, 1))
             ) AS s(c)) AS sub(col, counter);

LiveDemo3

You could have the application that collects the data count the unfinished rows and save it with the record. You could also create a calculated field to store the saved percentage value automatically when the record is saved.

Example with 3 columns:

CREATE TABLE [dbo].[Table](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [COL_A] [varchar](10) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [COL_B] [nchar](10) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [COL_C] [nchar](10) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [PctSaved]  AS 
            ((case when [COL_A] IS NULL then (1) else (0) end
            +case when [COL_B] IS NULL then (1) else (0) end)
            +case when [COL_C] IS NULL then (1) else (0) end/(3.0)
            ) PERSISTED
) ON [PRIMARY]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM