postgres: use of CASE and ANY() in WHERE clause - sql

Is there some way to make this work?
SELECT
*
FROM table t
INNER JOIN othertable t2 USING (tid)
WHERE
t.tid =
CASE
WHEN t2.someboolval THEN ANY(ARRAY[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16])
ELSE ANY(ARRAY[77,66])
END
Unfortunately I can't just do t.tid = CASE WHEN t2.someboolval THEN 1 ELSE 2 END because I need to match against an array. Is this doable?

Use AND/OR. Something like:
SELECT
*
FROM table t
INNER JOIN othertable t2 USING (tid)
WHERE
t2.someboolval AND t.tid IN (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16)
OR NOT (t2.someboolval) and t.id IN (77,66)
Edit: formatted

You have to change place of ANY:
SELECT
*
FROM table t
INNER JOIN othertable t2 USING (tid)
WHERE
t.tid =
ANY(CASE
WHEN t2.someboolval THEN ARRAY[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
ELSE ARRAY[77,66]
END)

Related

Postgresql SQL Select items from table1 based on a condition from table2

I am trying to select items from table1 which has a child table2 there is a third table3 involved.
Select j.ccmasterid,
(Select sum(i.ccmatpullqty) From table2 i
Where i.ccmasterid = j.ccmasterid) pulled
from table1 j
INNER JOIN table3 s on j.ccstatus = s.sysstatusid and s.ccopenjob=false
where j.ccmasterid LIKE 'W%' and pulled = 0
This generates an error:
ERROR: column "pulled" does not exist
LINE 6: where j.ccmasterid LIKE 'W%' and pulled = 0
If I take the "and pulled = 0" out the the query, it works as one would expect producing a list of records from table1 with the sum of the values in table2 as pulled.
ccmasterid pulled
W106063 0
W100553 9
W100685 1
WHAT I can't figure out is how to select based on pulled being 0.
Change this query into a subquery, and move WHERE condition to the outer query:
SELECT * FROM (
Select j.ccmasterid,
(Select sum(i.ccmatpullqty) From table2 i
Where i.ccmasterid = j.ccmasterid) pulled
from table1 j
INNER JOIN table3 s on j.ccstatus = s.sysstatusid and s.ccopenjob=false
where j.ccmasterid LIKE 'W%'
) x
WHERE pulled = 0
Avoid the correlated subquery which runs for every row in outer query and not once if joining to an aggregate query with GROUP BY clause:
SELECT j.ccmasterid
FROM table1 j
INNER JOIN table3 s
ON j.ccstatus = s.sysstatusid AND s.ccopenjob = false
INNER JOIN
(SELECT i.ccmasterid, SUM(i.ccmatpullqty) AS pulled
FROM table2 i
GROUP BY i.ccmasterid
) AS agg
ON agg.ccmasterid = j.ccmasterid
WHERE j.ccmasterid LIKE 'W%' AND agg.pulled = 0
Even use CTE
WITH agg AS
(SELECT i.ccmasterid, SUM(i.ccmatpullqty) AS pulled
FROM table2 i
GROUP BY i.ccmasterid)
SELECT j.ccmasterid
FROM table1 j
INNER JOIN table3 s
ON j.ccstatus = s.sysstatusid AND s.ccopenjob = false
INNER JOIN agg
ON agg.ccmasterid = j.ccmasterid
WHERE j.ccmasterid LIKE 'W%' AND agg.pulled = 0

Returning narrowed down SELECT based on associated table

I have a join query I use to pull data from another table:
SELECT [THEME].[NAME],
[THEMETYPE].[TYPE]
FROM [THEME]
LEFT OUTER JOIN [THEMETYPE]
ON [THEME].[THEMETYPEID] = [THEMETYPE].[PK_THEMETYPE]
WHERE COALESCE([THEME].[THEMETYPEID], 'null') LIKE '%'
ORDER BY CASE
WHEN [THEMETYPE].[TYPE] IS NULL THEN 1
ELSE 0
END,
[THEMETYPE].[TYPE]
I need to add the ability to narrow it down if a 3rd tables values match up:
Where producttheme.productid = variable-paramater-here
AND producttheme.themeid = theme.pk_theme
Here is a pic of the table:
So if the 1 is chosen above, it will return all [Theme].[Name] and the associated [ThemeType].[Type] where The ThemeId is associated with ProductId = 1
Edit: to be more clear ThemeId is the Primary key in the Theme table where Theme.Name exists.
This would give you some idea, please adjust the column names accordingly:
SELECT [Theme].[Name], [ThemeType].[Type]
FROM [Theme]
Left Outer Join [ThemeType]
ON [Theme].[ThemeTypeId] = [ThemeType].[PK_ThemeType]
join ProductTheme PT
on PT.ProductID=ThemeType.ProductID
WHERE ProductTheme.ProductID = VARIABLE-PARAMATER-HERE AND ProductTheme.ThemeId = Theme.PK_Theme
ORDER BY [ThemeType].[Type]
Depending on whether or not you need the WHERE condition before you add the 3rd table, you can try one of these 2 options:
SELECT *
FROM TABLE1 T1
LEFT OUTER JOIN TABLE2 T2
ON T1.FIELDA = T2.FIELDA
INNER JOIN TABLE3 T3
ON T1.FIELDA = T3.FIELDA
WHERE T1.FIELDB = 'aaa'
AND T3.FIELDC = 12
or:
SELECT *
FROM (SELECT T1.FIELDA,
T2.FIELDB
FROM TABLE1 T1
LEFT OUTER JOIN TABLE2 T2
ON T1.FIELDA = T2.FIELDA
WHERE T1.FIELDC = 'aaa')T3
INNER JOIN TABLE3 T4
ON T3.FIELDA = T3.FIELDA
AND T4.FIELDC = 12
I hope this gives you something to work with.
If you provide some sample data, I can set up a working example.

SQL join with case

How can I modify the join clause with a case clause; for example I want the table to join another column if column1 is null such as:
SELECT * FROM MYTABLE
LEFT JOIN OTHERTABLE ON
CASE WHEN MYTABLE.A IS NULL THEN MYTABLE.B = OTHERTABLE.A
ELSE MYTABLE.A IS NOT NULL THEN MYTABLE.A = OTHERTABLE.A
(totally made that up,sorry for syntax errors :))
Try this one:
SELECT *
FROM MyTable M
LEFT JOIN OtherTable O ON(CASE WHEN M.A IS NULL THEN M.B ELSE M.A END) = O.A
SELECT * FROM MYTABLE
LEFT JOIN OTHERTABLE ON COALESCE(MYTABLE.A, MYTABLE.B) = OTHERTABLE.A
Just try below code :
SELECT * FROM MYTABLE
LEFT JOIN OTHERTABLE ON OTHERTABLE.A = isnull(MYTABLE.A,MYTABLE.B)
You can use isnull() or coalesce() to check the null value.

How to simplify this query with sql joins?

my_table has 4 columns: id integer, value integer, value2 integer, name character varying
I want all the records that:
have the same value2 as a record which name is 'a_name'
have a field value inferior to the one of a record which name is 'a_name'
And I have satisfying results with the following query:
select t.id
from my_table as t
where t.value < ( select value from my_table where name = 'a_name')
and s.value2 = (select value2 from my_table where name = 'a_name');
But is it possible to simplify this query with sql joins ?
Joining on the same table is still too much intricate in my mind. And I try to understand with this example.
What I happened so far trying, is a result full of dupplicates:
select t2.id
from my_table as t
inner join my_table as t2 on t2.value2 = t.value2
where t2.value < ( select value from my_table where name = 'a_name');
I think this will solve your problem.
select t1.id
from my_table as t1
join my_table as t2
on t1.value2 = t2.value2
and t2.name = 'a_name'
and t1.value < t2.value
You should use self join instead of inner join see this
http://msdn.microsoft.com/en-us/library/ms177490%28v=sql.105%29.aspx
You can always get distinct results by calling "SELECT distinct t2.id ..."
However, that will not enhance your understanding of inner joins. If you are willing, keep reading on. Let's start by getting all records with name = 'a_name'.
SELECT a.*
FROM my_table as a
WHERE a.name = 'a.name';
A simpler way to perform your inner joins is to understand that the result for the above query is yet another table, formally known as a relation. You can think of it as joining on the same table, but an easier way to think of it is as "joining on the result of this query". Lets put this to the test.
SELECT other.id
FROM my_table as a,
INNER JOIN my_table as other ON other.value2 = a.value2
WHERE a.name = 'a_name'
AND other.value < a.value;
If the first query (all rows with name = 'a_name') has many results, you stand a good chance of the second query having duplicates, because the inner join between aliases 'a' and 'other' is a subset of their cross product.
Edits: Grammar, Clarity
please try this
select t.id
from my_table as t
inner join
(select value from my_table where name = 'a_name')t1 on t.value<t1.value
inner join
(select value2 from my_table where name = 'a_name')t2 on t.value2=t2.value2

Can I join to a table in ORACLE (10g) using a CASE clause in the ON statement (or even where clause as it's an inner join)

I'm trying to make the following code smaller. Is this possible?
select a.*
from table1 a
WHERE a."cola1" = 'valuea1'
UNION ALL
select a.*
from tablea1 a
inner join tablea2 b on a."cola2" = b."colb2"
WHERE a."cola1" = 'valuea2'
and b."colb3" = 'valueb3'
In effect I'm looking for records from table1 for value1 or value2, but for records matching value2 I want to apply 1 extra condition which involves a join to a 2nd table
Can this be done without a UNION clause?
A skeleton or what I'm trying to code is below....but it's not working naturally.
select a.*
from table1 a
inner join table2 b on a."cola1" = b."COLb1"
WHERE a."cola2" IN ('valuea1','valuea2')
and
CASE
WHEN a."cola2" = 'valuea2' THEN b."colb1" = 'valueb3'
ELSE 1=1
END CASE
I think CASE statements work in join conditions, but I'm not sure. But would this work for you?
select *
from table1 a
where a.cola1 = 'valuea1'
or (a.cola1 = 'valuea2'
and Exists(select 1
from table2 b
where a.cola2 = b.colb2
and b.colb3 = 'valueb3'
)
)
Edit: Wouldn't this simply work?
select a.*
from table1 a
Left Outer Join table2 b On (a.cola2 = b.colb2)
where a.cola1 = 'valuea1'
or (a.cola1 = 'valuea2' and b.colb3 = 'valueb3')
Overall you should follow Hosam's suggestion of rewriting the predicate entirely. But to explain your original problem further, the issue is that in SQL, CASE .. END is an expression and can only be used where any other expression could be used. A condition like "a=b" is two expressions connected by a logical operator. You may want to think of it as a boolean expression but that's not the way SQL views it.
You could accomplish what you want with CASE by using it as one of the two expressions in the condition, like so:
WHERE a."cola2" IN ('valuea1','valuea2')
and
b."colb1" = CASE
WHEN a."cola2" = 'valuea2' THEN 'valueb3'
ELSE b."colb1"
END CASE
(If it is possible for colb1 to include NULLs you would need to modify to handle that.)
You can achieve this by using left join and where condition
select a.*
from table1 a
left join tablea2 b on a."cola2" = b."colb2"
WHERE a."cola1" = 'valuea2'
and ( b."colb2" is null or b."colb3" = 'valueb3' )
OP: I've got a mini-workaround which goes close (This may only work given this is an inner join.)
select a.* from table1 a
inner join table2 b on a."cola1" = b."COLb1"
WHERE
(a."cola2" = 'valuea1')
OR (a."cola2" = 'valuea2' and b."colb1" = 'valueb3')
Sometimes writing code out can prompt some alternative thinking. Self-Therapy sort of. Thanks for your input.