Reputation: 1092
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.
Upvotes: 2
Views: 363
Reputation: 175686
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.
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)
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);
Upvotes: 4
Reputation: 6713
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]
Upvotes: 1