Hi Howard,

You should re-post this on the SQLite-users list <sqlite-users@mailinglists.sqlite.org>, it's more appropriate for it and you will get a lot more (and arguably better) responses from there. (I've gone ahead and included that list for you, just remove the dev list from replies to avoid duplication - thanks).

If I understand you properly, then what you need is a query that is fully listing either of two (or more) sets of filtered results, but not both / all.

Best would be to use a CTE or Sub-query set to get to something like this:

WITH A(y) AS (
    SELECT Y FROM T WHERE x = 1
), B(y) AS (
    SELECT Y FROM T WHERE x = 2
), C(y) AS (
    SELECT Y FROM T WHERE x = 3
), K(ca, cb, cc) AS (
    SELECT
        (SELECT COUNT(*) FROM A) AS AV,
        (SELECT COUNT(*) FROM B) AS BV,
        (SELECT COUNT(*) FROM C) AS CV
)
  SELECT Y FROM K,A WHERE K.ca > 0 AND K.cb = 0 AND K.cc = 0
UNION ALL
  SELECT Y FROM K,B WHERE K.ca = 0 AND K.cb > 0 AND K.cc = 0
UNION ALL
  SELECT Y FROM K,C WHERE K.ca = 0 AND K.cb = 0 AND K.cc > 0
;


A better method might be to simply check the existence and not pre-render the queries in the CTE (and perhaps make it so the deciding bit prefers A over B over C), something like this:

WITH K(ca, cb, cc) AS (
    SELECT
            (SELECT COUNT(*) FROM T WHERE T.x=1),
            (SELECT COUNT(*) FROM T WHERE T.x=2),
            (SELECT COUNT(*) FROM T WHERE T.x=3)
)
  SELECT Y FROM K,T WHERE K.ca > 0 AND T.x = 1
UNION ALL
SELECT Y FROM K,T WHERE K.ca = 0 AND K.cb > 0 AND T.x = 2
UNION ALL
SELECT Y FROM K,T WHERE K.ca = 0 AND K.cb = 0 AND T.x = 3
;

(You can see one can even remove the cc term from the K CTE)

It can obviously also be simplified sans CTE into just:

SELECT Y FROM T WHERE (SELECT COUNT(*) FROM T AS TA WHERE TA.x=1) > 0 AND x = 1
UNION ALL
SELECT Y FROM T WHERE (SELECT COUNT(*) FROM T AS TA WHERE TA.x=1) = 0 AND (SELECT COUNT(*) FROM T AS TB WHERE TB.x=2) > 0 AND x = 2
UNION ALL
SELECT Y FROM T WHERE (SELECT COUNT(*) FROM T AS TA WHERE TA.x=1) = 0 AND (SELECT COUNT(*) FROM T AS TB WHERE TB.x=2) = 0 AND x = 3


Note also that SQLite Query Planner is somewhat clever in that it should run those WHERE clause COUNT queries only once, but it's probably safer to force that behaviour by doing the count up in the CTE as in the first example - though I agree with James on an often-made point that one should only ever state the relation that is correct in the SQL, and the Query engine should be trusted with figuring out the most efficient route.

Good luck!


On 2017/04/11 2:37 AM, Howard Kapustein wrote:

I’ve got a query that I want to match a set of data based on WHERE x=foo but if 0 matches, I want to match WHERE x=bar

Programmatically it’s

list=FindByX(x=1)

if list.isempty

       list=FindByX(x=2)

return list

To make matters worse it’s actually a multi table join e.g.

SELECT * FROM A

  INNER JOIN B ON A.a=B.a

  INNER JOIN C ON B.b=C.b

  INNER JOIN X ON C.c=X.c

  WHERE X.x=?

In theory it’s logically

SELECT * FROM (

    IFNULL(SELECT * FROM A…WHERE X.x=foo, SELECT * FROM A…WHERE X.x=bar);

)

SQLITE has UNION which is the wrong verb. I want foo ELSE bar, not both, and not an interleaved result. All of just foo, or if no matches then all of just bar.

SQLITE’s WITH ctes seem like they might help, but in the end it still seems like >2 sub-queries to figure out the right set (foo vs bar), and a ton of complex SQL that’s uglier and questionable if perf any different than just doing the programmatic style with 1-2 SQL calls.

Any (sane) SQL solution for the query? Or is the programmatic approach the best answer?

  * Howard



_______________________________________________
sqlite-dev mailing list
sqlite-...@mailinglists.sqlite.org
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-dev

_______________________________________________
sqlite-users mailing list
sqlite-users@mailinglists.sqlite.org
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to