My SQL: Add if condition in where statement? - mysql

I want to select all customers or one customer from same SP. Right now we are mantaining two SPs to get these details:
i.e. To get all customers:
select id, name from customers
and to get one customer:
select id, name from customers
where id=id_from_input
To make them common, i thought of pasing id_from_input as null to get all customers. I tried several ways to make a conditional where statement in my sql that would work with this plan, but nothing is working. For example:
select id, name from customers
where if(id_from_input <> null) id=id_from_input; end if
gives me syntax error.
How can I make a where clause that returns all rows when id_from_input is null, the matching row otherwise?

The expression x <> null is never true; you can only use is null, or is not null, to test for null. Correcting your attempt gives:
select id, name from customers
where id_from_input is null or id = id_from_input
Or for something more terse (and IMHO elegant):
select id, name from customers
where id = coalesce(id_from_input, id)

Use CASE Statement to achieve your result :
SELECT id, name FROM customers WHERE id = CASE WHEN ISNULL(id_from_input,'')
<> '' THEN id_from_input ELSE id END

Try to use or:
select id, name
from customers
where id_from_input is null or id = id_from_input
if id_from_input is null then or clause will not be calculated.

Try this query
select id, name from customers where
CASE WHEN id_from_input<>null THEN id=id_from_input
ELSE id END

Related

If value exists in other table, return another value from that table

I currently have the following SQL query:
SELECT video_calls.initiated_user_id AS user_id,
(CASE
WHEN EXISTS
(SELECT *
FROM patients
WHERE patients.id = video_calls.initiated_user_id)
THEN 'patient'
ELSE (CASE
WHEN EXISTS
(SELECT *
FROM backend_users
WHERE backend_users.id = video_calls.initiated_user_id)
THEN "%%backend%%"
ELSE "unknown"
END)
END) AS user_type
FROM video_calls
WHERE id='7f350a98-93d3-4d21-80a8-6cda3e47a4c0'
UNION
SELECT user_id,
user_type
FROM channel_joins
WHERE channel_id='7f350a98-93d3-4d21-80a8-6cda3e47a4c0'
In the line, where it currently says THEN "%%backend%%" I'd like to return the column backend_users.backend_type instead, for the corresponding row where the value video_calls.initiated_user_id has been found. I suppose I need to work with a JOIN here, but I currently can't figure out where exactly.
You are already using a correlated subquery. You can use that to get the value:
ELSE (SELECT COALESCE(MAX(bu.backend_type), 'unknown')
FROM backend_users bu
WHERE bu.id = video_calls.initiated_user_id
)
Note the use of MAX(). This ensures that exactly one value is returned. If no rows match, the MAX() returns NULL, so 'unknown' is returned.
This has one slight nuance from your pseudo-code. If the matching row is NULL, then this returns 'unknown' rather than NULL. If that is an issue, the logic in the subquery can be tweaked.

Unsupported SubQuery Expression ''Fashion'': SubQuery expression refers to Outer query expressions only

I am Using below query :
select UserId, Category from customer_data
where (Category in ('Fashion','Electronics'))
and (Action in ('Click','AddToCart','Purchase'))
and customer_data.UserId not in (select ustomer_data.UserId from customer_data where customer_data.Category='Fashion' and customer_data.Category='Electronics') ;
Getting below error :
hive> Unsupported SubQuery Expression ''Fashion'': SubQuery expression refers to Outer query expressions only.
I am not sure about this error , do I need to use table name for each cloumn like customer_data.Category in outer query also ?
Could you please help ?
Sample data :
UserId,ProductId,Category,Action
1,111,Electronics,Browse
2,112,Fashion,Click
3,113,Kids,AddtoCart
4,114,Food,Purchase
5,115,Books,Logout
6,114,Food,Click
7,113,Kids,AddtoCart
8,115,Books,Purchase
9,111,Electronics,Click
10,112,Fashion,Purchase
3,112,Fashion,Click
12,113,Kids,AddtoCart
desired output :
Output File
• userID
• category
Use analytic functions to calculate fashion_flag per UserId:
select UserId, Category
from
( --calculate User level flags
select UserId, Category,
max(fashion_flag) over (partition by UserId) as user_fashion_flag,
max(electronics_flag) over (partition by UserId) as user_electronics_flag
from
(--maybe you do not need this subquery, if case will work inside max() over
select UserId, Category,
case when Category='Fashion' then 1 else 0 end fashion_flag,
case when Category='Electronics' then 1 else 0 end electronics_flag
from customer_data
where (Category in ('Fashion','Electronics'))
and (Action in ('Click','AddToCart','Purchase'))
) s
) s
where user_fashion_flag+user_electronics_flag=1 --not allow two flags at a time
;

How do I search for a value in a column and also get count of the column in a single SQL query

I have a table of following structure in MySQL 5.7:
CREATE TABLE `post_like` (
`post_title_id` varchar(255) NOT NULL,
`user_name` varchar(50) NOT NULL,
PRIMARY KEY (`post_title_id`,`user_name`),
) ENGINE=InnoDB;
and I have the following data
post_title_id user_name
new-story mani27
new-story manipal
some-story manipal
I am trying to get the count of likes for a particular story and also if a particular user has liked the story in a function likeStatus(user_name, post_title_id)
Suppose likeStatus(mani27, new-story) would result in:
count status
2 1
I am using the following query right now and it works right:
SELECT COUNT(user_name) AS count,
COUNT(CASE WHEN `user_name` = ? THEN 1 ELSE null END) as status
FROM post_like WHERE post_title_id = ?
But this would execute the case function on all the rows in the table rather than searching the indexed column user_name.
I could use two different queries to get count and status of username liking a post, but that would take much more time than this. So, is there an optimised way to get this output?
I didn't check the query but this should give you an idea. Try Group By
SELECT COUNT(user_name) AS count,
COUNT(CASE WHEN `user_name` = ? THEN 1 ELSE null END) as status
FROM post_like GROUP BY post_title_id HAVING post_title_id=?
But this would execute the case function on all the rows in the table
rather than searching the indexed column user_name
When you group by basing on post_title_id= and then applying count functions on them, number of row searches for username can be reduced to rows in that group
Add your condition inside CASE not in WHERE, then make sure you use DISTINCT to avoid duplicates:
SELECT COUNT(DISTINCT user_name) AS count,
COUNT(CASE WHEN `user_name` = ? AND post_title_id = ? THEN 1 ELSE null END) as status
FROM post_like
You don't have to scan all the records to see if a user liked the post. Just use a subquery on the select list. That should use the primary key index.
Something like this should work
SELECT COUNT(*),
(SELECT COUNT(*) FROM post_like WHERE postid=? AND userid=?)
WHERE postid=?

Getting data between a range and when one of the range is null

Need to get the data from DB based on the a column and the range is provided.
I have a student table with ID in it.
when i query the table on given range of id i.e between 10-20 then it is returning correct results.
ID is Varchar
SELECT *
FROM student
WHERE id BETWEEN 'AB10' AND 'AB20'
but lets suppose one of the range is not provided as range
1. ID from empty
2. ID to AB20
then ideally it should return from starting one to the provided id but i am getting no rows.
My query is
SELECT *
FROM student
WHERE id BETWEEN 'null' AND 'AB20'
One way is to just stop using BETWEEN; it's just the contraction of two Boolean's anyway, split them out.
select *
from student
where ( id >= :start_id
or :start_id is null
)
and ( id <= :end_id
or :end_id is null
)
If start_id is null then you'll get everything less than end_id, i.e. everything from the "start" of your sequence to end_id.
If end_id is null then you'll get everything greater than start_id.
If both are null then you'll get everything.
We're all assuming here, based on your question that the column ID has some intrinsic meaning; if it doesn't then this won't work.
You have to provide some value for between clause, null is no value.
You can change you code to cope up with the situation, something like this can help
select *
from student
where id between nvl(:from_value, (select min(id) from student))
and nvl(:to_value, (select max(id) from student));
as per Ben's suggestion, better to use COALEASE
select *
from student
where id between COALESCE(:from_value, (select min(id) from student))
and COALESCE(:to_value, (select max(id) from student));
COALESCE returns the first not null value from the list.

MySQL - tell if column _all_ has same value

I'm trying to write a query like
if (select count(*) from Users where fkId=5000 and status='r') =
(select count(*) from Users where fkId=5000) then ..
in just one query.
What this means is, if all the rows that have fkId=5000 also have status=r, then do something.
There can be any number of rows with fkId=5000, and any fraction of those rows could have status=r, status=k, status=l, status=a etc. I'm interested in the case where ALL the rows that have fkId=5000 also have status=r (and not any other status).
The way I'm doing it now is
how many rows with id=5000 and status = 'r'?
how many rows with id=5000?
are those numbers equal? then ..
I'm trying to figure out how to rewrite this query using only 1 query, instead of 2. Keyword ALL didn't seem to be able to write such a query (<> ALL is equivalent to NOT IN). I tried a couple of GROUP BY formulations but could not get the correct result to appear.
The most efficient way to do this is:
if not exists (select 1
from users
where fkid = 5000 and (status <> 'r' or status is null)
)
This will stop the query at the first non-matching row.
I suggest you to check for any rows with status not equal to 'r'
SELECT count(*)>0 FROM Users WHERE fkId = 5000 AND status != 'r'
In the following case, if the number 1 is "true" (which it is) then you'll get Yes back, and if not you'll get No back:
SELECT IF(1, 'Yes', 'No') AS yesorno
(Go ahead -- try it!)
In your case however, the following would be more appropriate:
SELECT IF (
(SELECT COUNT(*) FROM Users WHERE fkId=5000 AND status IN('r') AND status NOT IN('1', 'a', 'k')) = (SELECT COUNT(*) FROM Users WHERE fkId=5000),
'They are equal.',
'They are not equal.'
)
AS are_they_equal
By adding AS, you can manipulate the name of the "column" that's returned to you.
Hope that helps... Also, see this page if you'd like more info.
:)
EASY!
Simply join back to the same table. Here is the complete code for testing:
CREATE TABLE Users(id int NOT NULL AUTO_INCREMENT, fkID int NOT NULL, status char(1), PRIMARY KEY (id));
INSERT Users (fkID, status) VALUES (5000, 'r');
INSERT Users (fkID, status) VALUES (5000, 'r');
INSERT Users (fkID, status) VALUES (5000, 'r');
-- The next query produces "0" to indicate no miss-matches
SELECT COUNT(*) FROM Users u1 LEFT JOIN Users u2 ON u1.id=u2.id AND u2.status='r' WHERE u1.fkID=5000 AND u2.id IS NULL;
-- now change one record to create a miss-match
UPDATE Users SET status='l' WHERE id=3 ;
-- The next query produces "1" to indicate 1 miss-match
SELECT COUNT(*) FROM Users u1 LEFT JOIN Users u2 ON u1.id=u2.id AND u2.status='r' WHERE u1.fkID=5000 AND u2.id IS NULL;
DROP TABLE Users;
So all you need to test for in the result is that it's 0 (zero) meaning everything has fkID=5000 also has status='r'
If you properly index your table then joining back to the same table is not an issue and certainly beats having to do a 2nd query.
Besides the NOT EXISTS version - which should be the most efficient as it does no counting at all and exits as soon as it finds a value that doesn't match the conditions, there is one more way, that will work if status is not nullable and will be efficient if there is an index on (fkId, status):
IF EXISTS
( SELECT 1
FROM Users
WHERE fkId = 5000
HAVING MIN(status) = 'r'
AND MAX(status) = 'r'
)
There is one difference though. The above will show false if there are no rows at all with fkId=5000, while the NOT EXISTS version will show true - which is probably what you want anyway.