1

I use this select on my SQLite database:

SELECT attributeX
 FROM table
 GROUP BY attributeX
 HAVING COUNT(attributeY) = 0;

My table:

  attributeX (type integer) |  attributeY (type text)
----------------+-------
  1452 | aaa            
  1452 | bbb
  1452 | 
  1452 | bbb
  1453 |
  1453 |   
  1453 |
  1454 | eee
  1454 | fff
  1455 | iii
      .... 

I get result 1453 only when all items in column attributeY are empty for 1453. So in SQLite I get result 1453.

I have the same database only with myschema in PostgreSQL. I use this SELECT:

SELECT attributeX
 FROM myschema.table
 GROUP BY attributeX
 HAVING COUNT(attributeY) = 0;

and I get:

 attributeX
------------
(0 rows)

How to select in PostgreSQL?

SQLite schema of this table:

CREATE TABLE table (
 attribute1 INTEGER PRIMARY KEY,
 attributeX INTEGER NOT NULL,
 attribute2 TEXT NOT NULL,
 attributeY TEXT
);

CREATE INDEX table_attributeY_idx ON table (attributeY);
CREATE INDEX table_attribute2_idx ON table (attribute2);
CREATE INDEX table_attributeX_idx ON table (attributeX);

PostgreSQL schema of this table:

Table "myschema.table"
        Column  |          Type         |                                    Modifier                             | Storage  | Desc
----------------+-----------------------+-------------------------------------------------------------------------+----------+-------
 attribute1     | integer               | not null implicitly nextval('myschema.table_attribute1_seq'::regclass)  | plain    |
 attributeX     | integer               | not null                                                                | plain    |
 attribute2     | character varying(32) | not null                                                                | extended |
 attributeY     | text                  |                                                                         | extended |
Indexy:
    "table_attribute1_pkey" PRIMARY KEY, btree (attribute1)
    "fki_table_attributeX_fkey" btree (attributeX)
Conditions for foreign key:
    "table_attributeX_fkey" FOREIGN KEY (attributeX) REFERENCES myschema.table2(attributeX2) ON UPDATE RESTRICT ON DELETE CASCADE
Has OID: no

Insert:

INSERT INTO myschema.table (attributeX, attribute2, attributeY)
VALUES (%s, %s,%s)... (python script)
  • Please post a sqlfiddle.com demonstrating a self-contained test case. Per my answer, I can't see this issue hwne I test. – Craig Ringer Apr 7 '15 at 14:29
  • Self contained. Include the values. Preferably provide a sqlfiddle.com . something other people can run to see the same results you see. – Craig Ringer Apr 7 '15 at 16:10
  • Are you actually inserting NULL values, or empty strings? – CL. Apr 8 '15 at 8:04
3

You explain:

I get result 1453 only when all items in column attributeY are empty for 1453.

But that's incorrect. Bold emphasis mine. The aggregate function count returns (per documentation):

number of input rows for which the value of expression is not null

The same is true for SQLite (per documentation):

The count(X) function returns a count of the number of times that X is not NULL in a group.

You obviously have one or more rows with a non-null value in attributeY in your Postgres table - probably an empty string ''.

Test with:

SELECT *
FROM   myschema.table
WHERE  attributeX = 1453
AND    attributeY IS NOT NULL;

Be sure to understand the difference between "empty" ('') and NULL:

Empty strings are character types (typically text, varchar or char) with 0 characters ('') - so basically "nothing", the equivalent of 0 for a numeric data type. NULL is possible for any data type and means "unknown". Some clients have a hard time making the difference clear in their display.

Alternative query

To find attributeX where all attributeY are either empty or NULL use this alternative query:

SELECT attributeX
FROM   myschema.table
GROUP  BY attributeX
HAVING NOT bool_or(attributeY <> '');

The expression attributeY <> '' is only true for non-null, non-empty attributeY. The aggregate function bool_or returns (per documentation):

true if at least one input value is true, otherwise false

Those are the results we exclude (NOT ...) and return the rest. Voilá.

There are many other (less elegant) ways to achieve the same. Like:

... HAVING count(CASE WHEN attributeY <> '' THEN 1 END) = 0;
... HAVING count(attributeY <> '' OR NULL) = 0;
... HAVING count(NULLIF(attributeY, '') = 0;

More:

  • That's what I thought too, and why I was pushing for proper sample data. But that doesn't explain the difference observed between SQLite and PostgreSQL. I wondered if SQLite treated '' as null for the purpose of count but it doesn't, it correctly counts empty strings. – Craig Ringer Apr 8 '15 at 0:54
  • @CraigRinger: The difference between SQLite and PostgreSQL is rather "claimed" than "observed". If the Postgres DB has different data, then that's the extent of the difference here. Of course, a clean test case would have solved the problem to begin with - and we would have never seen it here ... – Erwin Brandstetter Apr 8 '15 at 1:11
  • @Erwin Brandstetter Thanks. When I use your select with IS NOT NULL I get correct results "1453" where attributeY is empty on all rows, but I have the same result when I use '' too. I do not understand because it does not work with HAVING COUNT? :-( Please help me... – user41060 Apr 10 '15 at 16:19
  • @user41060: I added an alternative query. You need to understand the concepts of "NULL" and "empty" to avoid constant headache when working with SQL. – Erwin Brandstetter Apr 10 '15 at 16:25
  • @ErwinBrandstetter Thank you very very much! – user41060 Apr 10 '15 at 20:58
2

It works for me with a similarly created structure. You didn't provide DDL and INSERTs for your table so I just made up a shorter one that should have the same effect:

$ sqlite3 test.db
SQLite version 3.8.8.3 2015-02-25 13:29:11
sqlite> CREATE TABLE test(x integer, y text);
sqlite> INSERT INTO test(x, y) VALUES (1, '');
sqlite> INSERT INTO test(x, y) VALUES (2, NULL);
sqlite> select x from test group by x having count(y) = 0;
2

and

$ psql -q regress
regress=> CREATE TABLE test(x integer, y text);
regress=> INSERT INTO test(x, y) VALUES (1, '');
regress=> INSERT INTO test(x, y) VALUES (2, NULL);
regress=> select x from test group by x having count(y) = 0;
 x 
---
 2
(1 row)

In both cases the results match what I would've expected.

I was initially wondering if your SQLite was treating '' and NULL as equivalent, but my test above suggests that it behaves as per the standard, treating '' as non-null for the purpose of count.

  • Thanks, I have added database structures and INSERT... I really do not get any results when I use this select... – user41060 Apr 7 '15 at 16:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.