SQL WHERE condition when one does not return true, then try other - sql

I have to query a table based on two fields such that if first field matches then don't check the second but if first field does not match then check if second field matches for a value
something like:
SELECT * FROM table
WHERE cart_id=389 OR (cart_id IS NULL AND user_id=26)
But if first condition succeeds, it must not check for second condition
Example:
Suppose the following is my table
id | cart_id | user_id
1 | 389 | 26
2 | null | 26
3 | 878 | 26
on querying for cart_id = 389 and user_id = 26, I should get back only record 1 and NOT 2
on querying for cart_id = 1 and user_id = 26, I should get back only records 2 and NOT 1 and 3

The only way I can think of, is to do this in two steps and check the result of the first step in the second:
with the_cart as (
SELECT *
FROM the_table
WHERE cart_id=389
)
select *
from the_cart
union all
select *
from the_table
where cart_id IS NULL
AND user_id=26
and not exists (select * from the_cart);
If the first query (using cart_id=389) returns something the second query from the union will not be run (or more precisely return no rows) due to the not exists() condition.
Online example

Based on your updated example data, your where clause would be:
WHERE cart_id = 389 and user_id = 26
but given how trivial that is, it’s difficult to believe that’s really what you’ve been asking all along.
===
Updated based on latest example…
WHERE (cart_id = 389 and user_id = 26)
OR (cart_id is null and user_id = 26)

Related

Filtering a column based on having some value in one of the rows in SQL or Presto Athena

I am trying in Athena to output only users which have some specific value in them but not in all of the rows
Suppose I have the table below.
I want all users which have value '100' in at least one of their rows but also having in other rows value different than 100.
user | value
A | 1
B | 2
A | 100
D | 3
A | 4
C | 3
C | 5
D | 100
So in this example I would want to get only users A and D because only them having 100 and none 100.
I tried maybe grouping by user and creating an array of values per user and then checking if array contains 100 but I don't manage doing it presto.
Also I thought about converting rows to columns and then checking if one of columns equals 100.
Those solutions are too complex? Anybody knows how to implement them or anyone has a better simpler solution?
The users that have at least one value of 100 can be found with this SQL:
SELECT DISTINCT user
FROM some_table
WHERE value = 100
But I assume you are after all tuples of user and value where the user has at least one value of 100, this can be accomplished by using the query above in a slightly more complex query:
WITH matching_users AS (
SELECT DISTINCT user
FROM some_table
WHERE value = 100
)
SELECT user, value
FROM matching_users
LEFT JOIN some_table USING (user)
You can use sub query as below to achieve your required output=
SELECT * FROM your_table
WHERE User IN(
SELECT DISTINCT User
FROM your_table
WHERE Value = 100
)
If you just want the users, I would go for aggregation:
select user
from t
group by user
having sum(case when value = 100 then 1 else 0 end) > 0;
If 100 is the maximum possible value, this can be simplified to:
having max(value) = 100

Sort by specific order, including NULL, postgresql

best explained with an example:
So I have users table:
id name product
1 second NULL
2 first 27
3 first 27
4 last 6
5 second NULL
And I would like to order them in this product order: [27,NULL, 6]
So I will get:
id name product
2 first 27
3 first 27
1 second NULL
5 second NULL
4 last 6
(notice user id 3 can be before user id 2 since they both have the same product value)
Now without NULL I could do it like that:
SELECT id FROM users ORDER BY users.product=27, users.product=6;
How can I do it with NULL ?
p.s.
I would like to do that for many records so it should be efficient.
You can use case to produce custom sort order:
select id
from users
order by case
when product = 27
then 1
when product is null
then 2
when product = 6
then 3
end
As a note, you can follow your original approach. You just need a NULL-safe comparison:
SELECT id
FROM users
ORDER BY (NOT users.product IS DISTINCT FROM 27)::int DESC,
(user.product IS NULL)::int DESC,
(NOT users.product IS DISTINCT FROM 6)::int DESC;
The reason your version has unexpected results is because the first comparison can return NULL, which is ordered separately from the "true" and "false".

If no result then another query, can be combined to one query?

Phone_book
+----+---------+-----------+--------------+
| id | key | code | value |
+----+---------+-----------+--------------+
| 1 | MAX_VAL | 111 | reset |
+----+------+--------------+--------------+
| 2 | MIN_VAL | 222 | set |
+----+------+--------------+--------------+
| 3 | MIN_VAL | 0 | NA |
+----+---------+-----------+--------------+
Key and code combination is the primary key.
Requirement:
if KEY and CODE is present, return VALUE.
if KEY is present and CODE not exist return the VALUE of CODE 0.
Implementation:
Achieved this with using multiple query. Syntax used is for JPQL
1) "SELECT param FROM Phone_book param WHERE upper(key)=:paramKey AND code=:estCode";
if this returns null, while shoot another query
2) "SELECT param FROM Phone_book param WHERE upper(key)=:paramKey AND code=:O";
What I looking for :
Can I achieve this in one query, or a better way ?
Thanks in advance.
In Oracle SQL, the below will suffice your need. No need to write PLSQL for this.
SELECT key,
nvl(code,0) -- This will make sure if code is null then value is 0
FROM Phone_book
WHERE (key is not NULL AND CODE IS NOT NULL) -- This will help in fetching value when KEY and CODE is present
OR ( key is not null and code is null); -- This will help in fetching value when KEY is present and CODE is null.
MySQL Relevant because the question was tagged MySQL initially
You can give it a try:
SELECT
defaultTable.`key`,
COALESCE(queryTable.`value`,defaultTable.`value`) AS v
FROM
(
SELECT
`key`,
`value`
FROM Phone_book
WHERE UPPER(`key`) = ?
AND `code` = 0
) AS defaultTable
LEFT JOIN
(
SELECT
`key`,
`value`
FROM Phone_book
WHERE UPPER(`key`) = ?
AND `code` = ?
) AS queryTable
ON defaultTable.`key` = queryTable.`key`;
Note: Replace the question marks by your provided values.
When there doesn't exist any record for the supplied key and code values then queryTable.value will be NULL.
So COALESCE will pick the value from defaulTable if any.
select
case
when key is not null and code is not null then value
when key is not null and code is null then 0
end
from phone_book;
select value from (
select value, row_number() over (order by case when code = 0 then 2 else 1 end) rn
from phonebook pb
where upper(key) = :paramKey and (code = :estCode or code = 0))
where rn = 1
Select values having requested key and requested code or code 0. Sort them properly with function row_number and take first value.

how to select one tuple in rows based on variable field value

I'm quite new into SQL and I'd like to make a SELECT statement to retrieve only the first row of a set base on a column value. I'll try to make it clearer with a table example.
Here is my table data :
chip_id | sample_id
-------------------
1 | 45
1 | 55
1 | 5986
2 | 453
2 | 12
3 | 4567
3 | 9
I'd like to have a SELECT statement that fetch the first line with chip_id=1,2,3
Like this :
chip_id | sample_id
-------------------
1 | 45 or 55 or whatever
2 | 12 or 453 ...
3 | 9 or ...
How can I do this?
Thanks
i'd probably:
set a variable =0
order your table by chip_id
read the table in row by row
if table[row]>variable, store the table[row] in a result array,increment variable
loop till done
return your result array
though depending on your DB,query and versions you'll probably get unpredictable/unreliable returns.
You can get one value using row_number():
select chip_id, sample_id
from (select chip_id, sample_id,
row_number() over (partition by chip_id order by rand()) as seqnum
) t
where seqnum = 1
This returns a random value. In SQL, tables are inherently unordered, so there is no concept of "first". You need an auto incrementing id or creation date or some way of defining "first" to get the "first".
If you have such a column, then replace rand() with the column.
Provided I understood your output, if you are using PostGreSQL 9, you can use this:
SELECT chip_id ,
string_agg(sample_id, ' or ')
FROM your_table
GROUP BY chip_id
You need to group your data with a GROUP BY query.
When you group, generally you want the max, the min, or some other values to represent your group. You can do sums, count, all kind of group operations.
For your example, you don't seem to want a specific group operation, so the query could be as simple as this one :
SELECT chip_id, MAX(sample_id)
FROM table
GROUP BY chip_id
This way you are retrieving the maximum sample_id for each of the chip_id.

Postgres: regex and nested queries something like Unix pipes

Command should do: Give 1 as output if the pattern "*#he.com" is on the row excluding the headings:
user_id | username | email | passhash_md5 | logged_in | has_been_sent_a_moderator_message | was_last_checked_by_moderator_at_time | a_moderator
---------+----------+-----------+----------------------------------+-----------+-----------------------------------+---------------------------------------+-------------
9 | he | he#he.com | 6f96cfdfe5ccc627cadf24b41725caa4 | 0 | 1 | 2009-08-23 19:16:46.316272 |
In short, I want to connect many SELECT-commands with Regex, rather like Unix pipes. The output above is from a SELECT-command. A new SELECT-command with matching the pattern should give me 1.
Related
Did you mean
SELECT regexp_matches( (SELECT whatevername FROM users WHERE username='masi'), 'masi');
you obviously can not feed the record (*) to regexp_matches, but I assume this is not what your problem is, since you mention the issue of nesting SQL queries in the subject.
Maybe you meant something like
SELECT regexp_matches( wn, 'masi' ) FROM (SELECT whatevername AS wn FROM users WHERE username LIKE '%masi%') AS sq;
for the case when your subquery yields multiple results.
It looks like you could use a regular expression query to match on the email address:
select * from table where email ~ '.*#he.com';
To return 1 from this query if there is a match:
select distinct 1 from table where email ~ '.*#he.com';
This will return a single row containing a column with 1 if there is a match, otherwise no rows at all. There are many other possible ways to construct such a query.
Let's say that your original query is:
select * from users where is_active = true;
And that you really want to match in any column (which is bad idea for a lot of reasons), and you want just to check if "*#he.com" matches any row (by the way - this is not correct regexp! correct would be .*#he.com, but since there are no anchors (^ or $) you can just write #he.com.
select 1 from (
select * from users where is_active = true
) as x
where textin(record_out( x )) ~ '#he.com'
limit 1;
of course you can also select all columns:
select * from (
select * from users where is_active = true
) as x
where textin(record_out( x )) ~ '#he.com'
limit 1;