views:

107

answers:

3

Is there a way to get a column indicating the number of NULL fields in a row? This would be within a SELECT statement.

For Example:

Field1  Field2  Num_Null
-----------------------
NULL     "A"      1

UPDATE: I want this query so I can sort based on how many Affiliates sales there are of a given Book. So having 3 affiliates would be sorted higher than having 2, regardless of which ones. There are about seven affiliates in my database, and that's subject to grow. So any query requiring that each Affiliate field be specified would probably be too long

The table:

Affiliates_Cache - Primary key is Affiliate_ISBN, has the prices of the book on various affiliates (NULL if its not available). Affiliates_Cache is the one where i want to count the number of NULLs

+2  A: 

I'm not sure if there are neater methods, but this should work:

SELECT Field1, Field2, ISNULL(Field1) + ISNULL(Field2) Num_Null
FROM   YourTable;

Test case:

CREATE TABLE YourTable (Field1 varchar(10), Field2 varchar(10));

INSERT INTO YourTable VALUES (NULL, 'A');
INSERT INTO YourTable VALUES ('B', 'C');
INSERT INTO YourTable VALUES ('B', NULL);
INSERT INTO YourTable VALUES (NULL, NULL);

Result:

+--------+--------+----------+
| Field1 | Field2 | Num_Null |
+--------+--------+----------+
| NULL   | A      |        1 |
| B      | C      |        0 |
| B      | NULL   |        1 |
| NULL   | NULL   |        2 |
+--------+--------+----------+
4 rows in set (0.00 sec)

UPDATE: Further to the updated question:

If you have columns in your table that look like affiliate_1, affiliate_2, etc, this is rarely a good idea as you would be mixing data with the metadata. In general, a recommended fix is to use another dependent table for the users-to-affiliates relationships, as in the following example:

CREATE TABLE users (
   user_id int, 
   user_name varchar(100),
   PRIMARY KEY (user_id)
) ENGINE=INNODB;

CREATE TABLE users_affiliates (
   user_id int, 
   affiliate_name varchar(100),
   PRIMARY KEY (user_id, affiliate_name),
   FOREIGN KEY (user_id) REFERENCES users (user_id)
) ENGINE=INNODB;

Then sorting the users table by the number of affiliates will look something like this:

SELECT    u.*, d_tb.num_aff
FROM      users
JOIN      (
             SELECT   user_id, COUNT(*) num_aff
             FROM     users_affiliates
             GROUP BY user_id
          ) d_tb ON (d_tb.user_id = u.user_id)
ORDER BY  d_tb.num_aff DESC;

The advantages are plenty, but most importantly it makes queries such as the above easy to write, and flexible enough to work with any number of affiliates (an not limited by the number of columns you allocated).

Daniel Vassallo
+1: I'm not aware of anything better, that I can think of
OMG Ponies
@OMG: If you don't know better, there probably isn't one :)
Daniel Vassallo
I'm flattered, but I'm here to learn as much as help
OMG Ponies
I'm going to accept this answer.. But out of curiosity why wouldn't SQL have some built in function that does this? It has .* for selecting rows
babonk
@babonk: there is seldom any call for doing the calculation. I've never needed it on properly normalized data. If you have columns Sales_01 .. Sales_12 for the 12 months of the year, then you might just need something like it - but it would be better to normalize your data.
Jonathan Leffler
The only real alternative is to use some CASE expressions, or functions such as NVL(). They don't provide sufficient (any?) benefit; your solution is about as neat as it gets.
Jonathan Leffler
Jonathan: I'm using it to sort based on how many Affiliates there are of a book. So having 3 affiliates would be sorted higher than having 2, regardless of which ones. [added to question]
babonk
Why is it a bad idea to mix Affiliate data with Book data? I always query all the affiliates for a given book, and I need to store whether they're NULL to know that the book is not available from the affiliate. Also, I store the DateTime that I query the affiliates, and it's the same for all the Affiliate sales of a book. So using your method I'd have to repeat the DateTime field in every row.
babonk
Also, just to clarify, the relationship is between Books and Affiliates rather than Users-Affiliates
babonk
@babonk: No, what I meant is mixing the *metadata* with the affiliate data. The `1` in `affiliate_1` is a data field, while `affiliate` is metadata (it is describing your data). You can still have a working database with your model, but there could be some problems. For example, what if you have just 3 affiliate columns, and you need a forth one? You'd have to create a new column (which is not ideal, for various reasons). If you add a new column, and you use the `ISNULL(Field1) + ISNULL(Field2)` solution I suggested in my answer, you'd have to update all queries to add a ` + ISNULL(Field3)`...
Daniel Vassallo
... you could easily forget one query, and then you'll have a bug in your code which is very difficult to discover... As for the `datetime` timestamp you write when you query the data, you could still put that in the `books` table, since it is functionally dependent on the book's primary key.
Daniel Vassallo
A: 

How about this Query ? (Referring to the Test case given by Daniel.)

SELECT Field1, Field2, (2 - (COUNT(ALL Field1)+COUNT(ALL Field2)))  Num_Null
FROM   @YourTable
GROUP BY Field1, Field2
Alex
There are about 7 fields I need to count, and the number of fields may be updated as I add more affiliates, so this query would seem to be overly long considering that. [Updated question]
babonk
A: 

Keep it simple and Standard:

SELECT Field1, Field2, 
       CASE WHEN Field1 IS NULL THEN 1 ELSE 0 END
       + CASE WHEN Field2 IS NULL THEN 1 ELSE 0 END
       AS Num__Null
  FROM YourTable;

Test case in full:

WITH YourTable (Field1, Field2)
     AS 
     (
      SELECT CAST(Field1 AS VARCHAR(10)), 
             CAST(Field2 AS VARCHAR(10))
        FROM (
              VALUES (NULL, 'A'),
                     ('B', 'C'),
                     ('B', NULL),
                     (NULL, NULL)
             ) AS YourTable (Field1, Field2)
     )
SELECT Field1, Field2, 
       CASE WHEN Field1 IS NULL THEN 1 ELSE 0 END
       + CASE WHEN Field2 IS NULL THEN 1 ELSE 0 END
       AS Num__Null
  FROM YourTable;
onedaywhen
There are about 7 fields I need to count, and the number of fields may be updated as I add more affiliates, so this query would seem to be overly long considering that. [Updated question]
babonk