Better use of redundant subquery in sql- case - sql

I have a question for SQL- Statements: Is it possible to "define a sub query" for multiple use in case. It sounds a bit confusing but with the following example I think it is clear what I have in mind:
select
Column1,
Column2,
Case
WHEN <BigSubquery> > 0 THEN <BigSubquery>
ELSE 0
END
from ...
How can I do this, or what can I use. I have such a query which works wonderful, but it is a huge code and not useable for maintenance.

If you are using a subquery, you should put the condition in the subquery. For instance, if you have:
(select sum(x) from . . . )
Then do:
(select (case when sum(x) > 0 then sum(x) else 0 end) from . . .

If you rewrite your query as
select
Column1,
Column2,
Case
WHEN Column3 > 0 THEN Column3
ELSE 0
END
from
(
select
Column1,
Column2,
BigSubquery as Column3
from ...
)
t
Then you avoid duplicating "BigSubquery", but you do duplicate the select list.

Related

PostgreSQL count multiple columns of the same table

I want to count some columns from a table in PostgreSQL.
For each column count I have some conditions and I want to have all in one query. My problem is the fact that I don't get the expected results in counting because I tried to apply all the conditions for the entire data set.
The table:
column1
column2
column3
UUID10
UUID20
UUID30
NULL
UUID21
NULL
NULL
UUID22
UUID31
UUID11
UUID20
UUID30
This is what I tried so far:
SELECT
COUNT(DISTINCT column1) AS column1_count,
COUNT(DISTINCT column2) AS column2_count,
COUNT(DISTINCT column3) AS column3_count
FROM TABLE
WHERE
column2 IN ('UUID20', 'UUID21', 'UUID22')
AND column1 = 'UUID10' -> this condition should be removed from this where clause
OR column3 IN ('UUID30', 'UUID31')
Result:
column1_count
column2_count
coumn3_count
2
3
2
The result in not correct because I should have column1_count = 1. I mean, this is what the query does, but is not what I intended. So I thought to have some constrains for column2 and column3 in a subquery, and having a another condition just for column1.
A second try:
SELECT *
FROM
(
SELECT
column1
column2,
column3
FROM TABLE
WHERE
column2 IN ('UUID20', 'UUID21', 'UUID22')
OR column3 IN ('UUID30', 'UUID31')
) x
WHERE
column1 = 'UUID10'
Result:
column1_count
column2_count
coumn3_count
1
1
1
Because the last condition on column1 is restricting my result, I end up having 1 for all the counts.
How can I apply different conditions for counting each column?
I would try not to use UNION if is possible. Maybe there can be made some subqueries in another way than what I tried so far. I just have to find a way for the constraint for the column1, to not be on the same WHEN clause as for the column2 and column3.
I think you want conditional aggregation:
SELECT COUNT(DISTINCT CASE WHEN column1 = 'UUID10' THEN column1 END) AS column1_count,
COUNT(DISTINCT column2) AS column2_count,
COUNT(DISTINCT column3) AS coumn3_count
FROM TABLE
WHERE column2 IN ('UUID20', 'UUID21', 'UUID22') OR
column3 IN ('UUID30', 'UUID31');
I assume that you are aware that COUNT(DISTINCT CASE WHEN column1 = 'UUID10' THEN column1 END) is not particularly useful code. It returns 1 or 0 depending on whether the value is present. I assume your code is actually more interesting.

SELECT from CTEs which might be null/undefined

Inside a function/stored procedure in Postgres 9.6 I want to grab data from two different tables using one CTE for each table like so:
WITH "CTE_from_table1" AS (SELECT column1, column2 FROM table1 WHERE id = $1),
"CTE_from_table2" AS (SELECT column1, column2 FROM table2 WHERE id = $2)
SELECT
COALESCE(
"CTE_from_table1".column1,
"CTE_from_table2".column1,
CASE WHEN "CTE_from_table1".column2 = 42 OR "CTE_from_table2".column2 = 42
THEN 'Something is 42' ELSE 'something else!' END
)
FROM "CTE_from_table1","CTE_from_table2";
(Data type of column1 and column2 are resp. identical for both tables: column1 being a text, column2 an integer.)
That works as long as both CTEs are defined. The problem is: The parameters $1 and/or $2 could be null or could contain IDs which are simply not there. In this case, I expect the result something else!, because the first two COALESCE parameters evaluate to null and the third, being the CASE WHEN, should go to its ELSE which would return something else!.
That's my theory. However, in practice I get null as soon as one of the CTEs is undefined/null. What can I do about that?
Your problem is the dreaded comma in the FROM clause. Simple rule . . . Never use commas in the FROM clause. In this case, you want an "outer cross join". The comma does an "inner cross join", so no rows are returned if either CTE has no rows.
Unfortunately, OUTER CROSS JOIN doesn't exist, so you can make do with FULL OUTER JOIN:
WITH "CTE_from_table1" AS (SELECT column1, column2 FROM table1 WHERE id = $1),
"CTE_from_table2" AS (SELECT column1, column2 FROM table2 WHERE id = $2)
SELECT COALESCE(ct1.column1, ct2.column1,
CASE WHEN 42 IN (ct1.column2, ct2.column2)
THEN 'Something is 42'
ELSE 'something else!'
END
)
FROM "CTE_from_table1" ct1 FULL OUTER JOIN
"CTE_from_table2" ct2
ON 1=1;
I'm not a big fan of mixing CASE and COALESCE(), so I'd be inclined to write:
SELECT (CASE WHEN ct1.column1 IS NOT NULL THEN ct1.column1
WHEN ct2.column1 IS NOT NULL THEN ct2.column1
WHEN 42 IN (ct1.column2, ct2.column2) THEN 'Something is 42'
ELSE 'something else!'
END)

How Do I Use Case Statement Column In Group By

As stated by the question, I'm trying to formulate a query that has a case statement in the column results, and then I want to include that column in the query's group by statement. To give a concrete example, here is all little of what my query looks like:
SELECT SOME_TABLE_ALIAS.COLUMN1, OTHER_TABLE_ALIAS.COLUMN2,
CASE
WHEN SOME_TABLE_ALIAS.COLUMN3 IS NOT NULL THEN 'A'
ELSE 'B'
END AS CASE_COLUMN
FROM SOME_TABLE SOME_TABLE_ALIAS
... (other table joins and where clauses)
GROUP BY SOME_TABLE_ALIAS.COLUMN1, OTHER_TABLE_ALIAS.COLUMN2, CASE_COLUMN
Before coming here, I checked out a few websites, including this one, to try solve my problem. I've tried adding another alias after the CASE keyword like is shown in the linked web page but have had no luck. The error message I continue to receive is the following:
[Error] Script lines: 127-151 ----------------------
CASE_COLUMN IS NOT VALID IN THE CONTEXT WHERE IT IS USED. SQLCODE=-206, SQLSTATE=42703, DRIVER=3.53.71
Has anyone else run into the issues I'm facing and been able to use a GROUP BY on the results of a CASE statement? Any help would be appreciated. Oh, and the DB2 version is a z/OS instance, version 10 (DSN10015)
The alias isn't available to use in the GROUP BY because when GROUP BY happens the alias isn't defined yet:
Here's the order:
1.FROM
2.WHERE
3.GROUP BY
4.HAVING
5.SELECT
6.ORDER BY
You can work around that with:
SELECT column1,column2,case_column
FROM (
SELECT SOME_TABLE_ALIAS.COLUMN1, OTHER_TABLE_ALIAS.COLUMN2,
CASE
WHEN SOME_TABLE_ALIAS.COLUMN3 IS NOT NULL THEN 'A'
ELSE 'B'
END AS CASE_COLUMN
FROM SOME_TABLE SOME_TABLE_ALIAS
... (other table joins and where clauses)
) a
GROUP BY COLUMN1, COLUMN2, CASE_COLUMN
Or just use the case you use in SELECT in GROUP BY
You can either use the case as is in the group by, like this:
SELECT SOME_TABLE_ALIAS.COLUMN1, OTHER_TABLE_ALIAS.COLUMN2,
CASE
WHEN SOME_TABLE_ALIAS.COLUMN3 IS NOT NULL THEN 'A'
ELSE 'B'
END AS CASE_COLUMN
FROM SOME_TABLE SOME_TABLE_ALIAS
... (other table joins and where clauses)
GROUP BY SOME_TABLE_ALIAS.COLUMN1, OTHER_TABLE_ALIAS.COLUMN2,
CASE
WHEN SOME_TABLE_ALIAS.COLUMN3 IS NOT NULL THEN 'A'
ELSE 'B'
END
or use a sub-query like this:
select COLUMN1, COLUMN2, CASE_COLUMN
from (
SELECT SOME_TABLE_ALIAS.COLUMN1, OTHER_TABLE_ALIAS.COLUMN2,
CASE
WHEN SOME_TABLE_ALIAS.COLUMN3 IS NOT NULL THEN 'A'
ELSE 'B'
END AS CASE_COLUMN
FROM SOME_TABLE SOME_TABLE_ALIAS
... (other table joins and where clauses)
) a
GROUP BY COLUMN1, COLUMN2, CASE_COLUMN

Faster way of doing multiple checks on one dataset

Is there a better way to rewrite the following:
SELECT DISTINCT Column1, 'Testing #1'
FROM MyTable
WHERE Column2 IS NOT NULL && Column3='Value'
UNION ALL
SELECT DISTINCT Column1, 'Testing #2'
FROM MyTable
WHERE Column3 IS NULL && Column2='Test Value'
UNION ALL
SELECT DISTINCT Column1, 'Testing #3'
FROM MyTable
Where ....
In have about 35 union all statements that all query the same table. I was wondering if there's an easier/faster way to do things.
Yes, you can rewrite it with case statements like this
SELECT Column1,
CASE WHEN Column2 IS NOT NULL AND Column3='Value' THEN 'Testing #1'
WHEN Column3 IS NULL AND Column2='Test Value' THEN 'Testing #2'
ELSE 'Testing #3' END as customcol
FROM MyTable
EDIT : Ok, i am making this edit because according to your comment, there are two issues we need to address. (I am leaving the original answer as it is in case it might help somebody.)
1) Result set should be filtered and there should be no else part.
This is actually achievable with this solution since else is optional and data can be filtered with a where clause at the end.
2) Being able to select the same row multiple times with different Testing # values if it matches the criteria.
This however is not achievable with my previous solution. So i thought of a different one. Hope it fits into your case. Here it is
S1 - Create a new table with Testing # values(Testing #1, Testing #2, Testing #3 etc.). Let's say this table is named Testing.
S2 - JOIN your main table (MyTable) with Testing table which contains Testing # values. So now you have every possible combination of real-data and testing values.
S3 - Filter the results you don't want to appear with a where clause.
S4 - Filter the real-data <-> testing combinations with an addition to where clause.
End query should look something like this :
SELECT M.Column1, T.TestingValue
FROM MyTable M
INNER JOIN Testing T ON 1=1
WHERE
(
(M.Column2 IS NOT NULL AND M.Column3='Value' AND T.TestingValue='Testing #1') OR
(M.Column3 IS NULL AND M.Column2='Test Value' AND T.TestingValue='Testing #2') OR
<conditions for other testing values>
)
AND
<other conditions>
I think this should work and produce the results you want. But since i don't have the data i am not able to run any benchmarks vs the union-based solution. So i don't have any scientific evidence to claim this is faster but it is an option. You can test both and use the better one.
It might be a little late but hope this solves your problem.
You can do this in one statement, but you want a different column for each test:
select column1,
(case when column2 is not null and column3 = 'Value' then 1 else 0
end) as Test1
(case when column3 is null and column3 = 'Test Value' then 1 else 0
end) as Test2,
. . .
from t;
Because you only want cases where things fail, you can put this in a subquery and test for any failure:
select *
from (select column1,
(case when column2 is not null and column3 = 'Value' then 1 else 0
end) as Test1
(case when column3 is null and column3 = 'Test Value' then 1 else 0
end) as Test2,
. . .
from t
) t
where test1 + test2 + . . . > 0

Multiplying two columns which have been calculated on a CASE statement

I am performing a SQL query in PostgreSQL using a CASE statement like this:
SELECT
CASE column1
WHEN something THEN 10
ELSE 20
END AS newcol1
CASE column12
WHEN something THEN 30
ELSE 40
END AS newcol2
COUNT(column3) newcol3
FROM table
GROUP BY newcol1,newcol2,newcol3
I need a fourth column which has to be the result of newcol2 * newcol3, how can I do that?
If I put (newcol2 * newcol3) AS newcol4 I get a syntax error.
You can always use a CTE to abstract things away to a different level, if that helps - something along the lines of ...
With CTE as
(
SELECT
CASE column1
WHEN something THEN 10
ELSE 20
END AS newcol1,
CASE column12
WHEN something THEN 30
ELSE 40
END AS newcol2,
column3,
FROM table
)
SELECT
newcol1, newcol2,
count(column3) as newcol3,
(newcol2 * newcol3) AS newcol4
FROM CTE
GROUP BY newcol1,newcol2,newcol3
A CTE is a valid approach, giving additional options.
For a simple case like this a plain subquery is simpler and slightly faster.
SELECT *, (newcol2 * newcol3) AS newcol4
FROM (
SELECT CASE column1
WHEN something THEN 10
ELSE 20
END AS newcol1
,CASE column12
WHEN something THEN 30
ELSE 40
END AS newcol2
,COUNT(column3) AS newcol3
FROM table
GROUP BY 1, 2
) AS sub
BTW: I removed newcol3 from GROUP BY, since you are running the aggregate function count() on it, which is slightly nonsensical.