select rows with 'where' that not match all condition - postgresql

I need to create a statement that select all row that mach 3 conditions or more.
This statement will be used by a high-performance JS function.
A record have some informations: foo, bar, baz, qux and fum.
I thought of 2 ways to do that and I chose the one that seems to me the best but maybe there is better ?
The first one (the best I think)
The db run a statement where the where clause match 3 conditions or more
I mean that for example the DB must returns all the rows where 3 fields or more match.
Below there is an example of the query, is there a way do it ? I think this one is pretty ugly... And if I need to add some informations...
If more than 1 record the JS script determine the good one (iterate over the results)
Query example :
select * from MyTable
where (foo='foo1' and bar='bar1' and baz='baz1')
or (foo='foo1' and bar='bar1' and qux='qux1')
or (foo='foo1' and bar='bar1' and fum='fum1')
or (bar='bar1' and baz='baz1' and qux='qux1')
or (bar='bar1' and baz='baz1' and fum='fum1')
[other or for 3 conditions match ...]
or (foo='foo1' and bar='bar1' and baz='baz1' and qux='qux1')
or (foo='foo1' and bar='bar1' and baz='baz1' and fum='fum1')
or (foo='foo1' and bar='bar1' and qux='qux1' and fum='fum1')
[other or for 4 conditions match ...]
or (foo='foo1' and bar='bar1' and baz='baz1' and qux='qux1' and fum='fum1') /* 5 conditions match */
The other one (surely the worst I think) :
DB return all rows that match at one condition
then the JS script determine the good one by iterating over all the results
Query example :
select * from MyTable
where foo='foo1' or bar='baz1' or baz='baz1' or qux='qux1'
Do you agree that the first one gives the best performance? If yes is there a better query ?

demo:db<>fiddle
SELECT
t.*
FROM mytable t
WHERE
(foo IS NOT DISTINCT FROM 'foo1')::int +
(bar IS NOT DISTINCT FROM 'bar1')::int +
(baz IS NOT DISTINCT FROM 'baz1')::int +
(qux IS NOT DISTINCT FROM 'qux1')::int +
(fum IS NOT DISTINCT FROM 'fum1')::int >= 3
IS NOT DISTINCT FROM checks for equality and considers NULL

You can check below query
select * from
(
select a.*,
case when foo='foo1' then 1 else 0 end +
case when bar='baz1' then 1 else 0 end +
case when baz='baz1' then 1 else 0 end +
case when qux='qux1' then 1 else 0 end +
case when fum='fum1' then 1 else 0 end as total
from MyTable a
where foo='foo1' or bar='baz1' or baz='baz1' or qux='qux1' or fum = 'fum1'
) as a
where total>=3;

Related

Postgresql - 9.1 IN Parameter changed the sorted order

I have some ids its already be in sorted order (65826 ,78555,456666) . I passed this input in select query to get data, but in the output the sort order will changed and returns result as (456666,65826,78555);
select sku_id from blc_sku where sku_id in(65826 ,78555,456666) but the output return as
sku_id
65286
78555
456666
but expected result,
sku_id
456666
65286
78555
Please help me to resolve this ...
t=> with v(a) as (values (2),(1),(3))
select * from v
t-> where a in (1,2,3)
t-> ;
a
---
2
1
3
(3 rows)
you see the result is in order the data is met, not in order you set in IN. Postgres does not guarantee such behaviour though (and you can't predict the order how data is met - it depends on cache and other things I have no knowledge about). If you want ordered result set use ORDER directive:
t=> with v(a) as (values (2),(1),(3))
select * from v
where a in (1,2,3)
order by a desc;
a
---
3
2
1
(3 rows)
In case you have an preordered list, you can join initial data against it, like here:
t=> with v(a) as (values (2),(1),(3))
, ordered (v,o) as (values(1,0), (2,1), (3,2))
select v.* from v
join ordered on v.a=ordered.v
order by o;
a
---
1
2
3
(3 rows)

Postgress by a CASE with DISTINCT in select

I have a query like below getting the error - 'SELECT DISTINCT, ORDER BY expressions must appear in select list'
select distinct name
from fruits
order by case
when name = 'mango' then 1
else 2
end
This results 4 records, say
apple, mango, pear and grape
How can I make sure I get Mango as the first record always and the rest follow. I tried using the case statement, but not able to get the desired results. Any ideas will be appreciated.
I believe this should accomplish what you describe as needing.
select distinct
name,
case name when 'Mango' then 1 else 2 end as fruitOrder
from fruits
order by
fruitOrder
If you need to always have 'mango' in first position, no matter the other rows, this could be a way:
with fruits(name) as (
select 'apple' from dual union all
select 'mango' from dual union all
select 'pear' from dual union all
select 'grape' from dual
)
select name
from fruits
order by case
when name = 'mango' then 1
else 2
end
If you need to add a DISTINCT, this should work:
select distinct name,
case
when name = 'mango' then 1
else 2
end orderCol
from fruits
order by orderCol
This will give you 'Mango' followed by the others in order;
WITH get_rows AS
(SELECT DISTINCT item_type
FROM the_item)
SELECT item_type
FROM
(SELECT 1 as seq, item_type
FROM get_rows
WHERE item_type = 'Mango'
UNION ALL
SELECT 2 as seq, item_type
FROM get_rows
WHERE item_type <> 'Mango')
ORDER BY seq, item_type

Using the result of a subquery in a CASE expression with T-SQL

I'm writing a query with some CASE expressions and it outputs helper-data columns which help me determine whether or not a specific action is required. I would like to know if I can somehow use the result of a subquery as the output without having to perform the same query twice (between WHEN (subquery) THEN and as the result after THEN)
The dummy code below describes what I'm after. Can this be done? I'm querying a MS2005 SQL database.
SELECT 'Hello StackOverflow'
,'Thanks for reading this question'
,CASE
WHEN
(
SELECT count(*)
FROM sometable
WHERE condition = 1
AND somethingelse = 'value'
) > 0 THEN
-- run the query again to get the number of rows
(
SELECT count(*)
FROM sometable
WHERE condition = 1
AND somethingelse = 'value'
)
ELSE 0
END
SELECT 'Hello StackOverflow'
,'Thanks for reading this question'
,CASE
WHEN
(
SELECT count(*)
FROM sometable
WHERE condition = 1
AND somethingelse = 'value'
) AS subqry_count > 0 THEN
-- use the subqry_count, which fails... "Incorrect syntax near the keyword 'AS'"
subqry_count
ELSE 0
END
Just use the subquery as the source you are selecting from:
SELECT 'Hello StackOverflow'
,'Thanks for reading this question'
,CASE subqry_count.Cnt
WHEN 0 THEN 0
ELSE subqry_count.Cnt
END
FROM ( SELECT count(*) AS Cnt
FROM sometable
WHERE condition = 1
AND somethingelse = 'value'
) subqry_count
As an aside, if you are just going to return 0 if the output from COUNT is 0, then you don't even need to use a CASE statement.

Conditional Union in T-SQL

Currently I've a query as follows:
-- Query 1
SELECT
acc_code, acc_name, alias, LAmt, coalesce(LAmt,0) AS amt
FROM
(SELECT
acc_code, acc_name, alias,
(SELECT
(SUM(cr_amt)-SUM(dr_amt))
FROM
ledger_mcg l
WHERE
(l.acc_code LIKE a.acc_code + '.%' OR l.acc_code=a.acc_code)
AND
fy_id=1
AND
posted_date BETWEEN '2010-01-01' AND '2011-06-02') AS LAmt
FROM
acc_head_mcg AS a
WHERE
(acc_type='4')) AS T1
WHERE
coalesce(LAmt,0)<>0
Query 2 is same as Query 1 except that acc_type = '5' in Query 2. Query 2 always returns a resultset with a single row. Now, I need the union of the two queries i.e
Query 1
UNION
Query 2
only when the amt returned by Query 2 is less than 0. Else, I don't need a union but only the resulset from Query 1.
The best way I can think of is to create a parameterised scalar function. How best can I do this?
You could store the result of the first query into a temporary table, then, if the table wasn't empty, execute the other query.
IF OBJECT_ID('tempdb..#MultipleQueriesResults') IS NOT NULL
DROP TABLE #MultipleQueriesResults;
SELECT
acc_code, acc_name, alias, LAmt, coalesce(LAmt,0) AS amt
INTO #MultipleQueriesResults
FROM
(SELECT
acc_code, acc_name, alias,
(SELECT
(SUM(cr_amt)-SUM(dr_amt))
FROM
ledger_mcg l
WHERE
(l.acc_code LIKE a.acc_code + '.%' OR l.acc_code=a.acc_code)
AND
fy_id=1
AND
posted_date BETWEEN '2010-01-01' AND '2011-06-02') AS LAmt
FROM
acc_head_mcg AS a
WHERE
(acc_type='4')) AS T1
WHERE
coalesce(LAmt,0)<>0;
IF NOT EXISTS (SELECT * FROM #MultipleQueriesResults)
… /* run Query 2 */

sql: how to select a row with a true value from a column of boolean values after the HAVING clause

HI have 3 product tables, each with 3 columns namely customer name, and boolean optout and blacklist. After the Having clause, there will be 3 rows for each customer name (assuming he has all 3 products).
How do I output a true if any of the boolean columns contains a true. I figured out by using the cast operation below, but think there should be a more elegant solution.
SELECT customer_name,
cast(int4(sum(cast(optout As int4))) As Boolean) As optout,
cast(int4(sum(cast(blacklist As int4))) As Boolean) As blacklist
FROM
(SELECT * FROM product1
UNION SELECT * FROM product2
UNION SELECT * FROM product3) AS temp1
GROUP BY customer_name, optout, blacklist
HAVING optout=true or blacklist=true;
Try the bool_or aggregate function, sounds like exactly what you're looking for:
SELECT customer_name,
bool_or(optout) As optout,
bool_or(blacklist) As blacklist
FROM
(SELECT * FROM product1
UNION SELECT * FROM product2
UNION SELECT * FROM product3) AS temp1
GROUP BY customer_name, optout, blacklist
HAVING optout=true or blacklist=true;
If I have understood the question correctly I think you just need a CASE statement in the SELECT e.g.
CASE
WHEN blackLIST = TRUE OR optout = TRUE THEN 1
ELSE 0
END