CASE or COALESCE performance in WHERE clause for MySQL - mysql

I'm wondering which is the better performance or best practice when dealing with multiple criteria in the WHERE clause and NULL values.
WHERE
u.id = COALESCE(user_id, u.id) AND
su.custom_id = COALESCE(student_number, su.custom_id)
OR
WHERE
CASE
WHEN user_id IS NOT NULL AND LENGTH(user_id) > 0
THEN
u.id = user_id
ELSE
su.custom_id = student_number
END

I would avoid both of those approaches. You should be able to accomplish what you are trying to do with a judicious use of AND, OR, IS NULL, and parentheses.
For example, you can rewrite this:
WHERE
u.id = COALESCE(user_id, u.id) AND
su.custom_id = COALESCE(student_number, su.custom_id)
Like this:
WHERE
(user_id IS NULL OR u.id = user_id) AND
(su.custom_id = student_number)
And you can rewrite this:
WHERE
CASE
WHEN user_id IS NOT NULL AND LENGTH(user_id) > 0
THEN
u.id = user_id
ELSE
su.custom_id = student_number
END
As this:
WHERE
(user_id IS NOT NULL AND LENGTH(user_id) > 0 AND u.id = user_id)
OR
(su.custom_id = student_number)

I would suggest using explain and seehow your query plan is. Like:
explain <your select query here>

Related

How can I write this code in MySQL in a shorter and better way?

There are 3 tables in the database and they have no relation. How can I write this code in a short form? Sorry for my bad english already.
MySQL Code;
SELECT
*,
(SELECT COUNT(c2.id) FROM comments AS c2 WHERE c2.phone_number = comments.phone_number AND c2.country_code = comments.country_code AND c2.approve = "0" AND c2.deleted_at IS NULL) AS comment_count,
(SELECT COUNT(f.id) FROM flag_numbers AS f WHERE f.phone_number = comments.phone_number AND f.country_code = comments.country_code AND comments.approve = "0" AND comments.deleted_at IS NULL) AS flag_count
FROM comments
WHERE approve = "0" AND deleted_at IS NULL
ORDER BY id DESC
Your code is pretty good, but the following is a bit simpler:
SELECT c.*,
COUNT(*) OVER (PARTITION BY c.phone_Number, c.country_code) as comment_count,
(SELECT COUNT(f.id)
FROM flag_numbers f
WHERE f.phone_number = c.phone_number AND
f.country_code = c.country_code
) AS flag_count
FROM comments c
WHERE c.approve = '0' AND c.deleted_at IS NULL
ORDER BY id DESC;
If approve is a number, then remove the single quotes. Don't quote number constants.
The differences:
Replacement of the first subquery with a window function for comment_count.
Removal of the redundant comment filters in the flag_count.

How to make query

review table has store_idx, user_idx etc...
I want to create a query sentence that gets information about the store to which the user has bookmarked with the user_id value entered.
The query sentence I made is
select A.store_name
, A.store_img
, count(B.store_idx) as review_cnt
from board.store A
Left
Join board.review B
On A.store_idx is B.store_idx
where store_idx is (select A.store_idx from bookmark where user_id = ?)
However, nothing came out as a result.
Help me..
Please use below Query:
SELECT store_name
, store_img
, SUM(review_cnt) AS review_cnt
FROM
( SELECT DISTINCT A.store_name
, A.store_img
, CASE WHEN B.store_idx IS NULL THEN 0 ELSE 1 END AS review_cnt
FROM bookmark br
JOIN board.store A
ON A.store_idx = br.store_idx
LEFT
JOIN board.review B
ON A.store_idx = B.store_idx
WHERE br.user_id = ?
)T
The WHERE clause is obviously filtering out all rows. We can't do much about that. But your query is also lacking a GROUP BY, the table aliases can be improved, and the join condition is not correct.
So, try this version:
select s.store_name, s.store_img, count(b.store_idx) as review_cnt
from board.store s left join
board.review r
on s.store_idx = r.store_idx
where b.store_idx in (select b.store_idx
from bookmark b
where b.user_id = ?
);

compare colums of two table if table1.row > table2.row

TABLE1
mybb_users
(id gcoins)
1 150
2 10
TABLE2
servercoins
(id prezzo)
1 150
2 70
I should compare this table for control if users have enough "gcoins" for buy "prezzo". If "gcoins" > "prezzo" it's ok but "gcoins" < "prezzo" print an alert with "You don't have enough gocins for this"
Try this...
SELECT
CASE WHEN ((SELECT gcoins
FROM TABLA1
WHERE TABLA1.id = <ID_USER>
AND gcoins >0) < ( SELECT prezzo
FROM TABLA2
WHERE TABLA2.id = <ID_PREZZO>))
THEN 'You dont have enough gocins for this'
ELSE 'something else...'
END
<ID_USER> and <ID_PREZZO> replace with your variables.
HERE you can test!
Hope this help!
This is a general query that will print out the status:
SELECT u.id, u.gcoins, s.prezzo,
CASE WHEN u.gcoins >= s.prezzo THEN 'OK' else 'Not Enough gcoins' END AS Status
FROM mybb_users u
INNER JOIN servercoins s ON u.id = s.id
If you want a query that just returns the rows that need the alert, then remove the second line, and add a WHERE clause instead:
SELECT u.id, u.gcoins, s.prezzo
FROM mybb_users u
INNER JOIN servercoins s ON u.id = s.id
WHERE u.gcoins < s.prezzo
This gives you a list of those who need to be warned that they don't have enough gcoins.

MYSQL SELECT is slow when crossing multiple tables

I have a mysql query which is to return the only 1 record that need to cross multiple table. However, the mysql query is slow when executing.
Query:
SELECT *,
(SELECT TreeName FROM sys_tree WHERE TreeId = Mktg_Unit_Booking.ProjectLevelId) AS PhaseName,
(CASE WHEN ProductType = 'U' THEN (SELECT UnitNo FROM prop_unit pu WHERE pu.UnitId = mktg_unit_booking.UnitId)
ELSE (SELECT BayNo FROM prop_car_park pcp WHERE pcp.CarParkId = UnitId) END) AS UnitNo,
(SELECT CustomerName FROM mktg_customer mc WHERE mc.CustomerId = mktg_unit_booking.CustomerId) AS CustomerName
FROM Mktg_Unit_Booking
WHERE IsDeleted <> '1' AND IsApproved = '1'
AND UnitId = 1110 AND ProductType = 'U'
ORDER BY UnitNo
I have run EXPLAIN in the query and I got this:
Any other suggestion on how to improve the speed of the query?
Thank you!
you are doing the cross product, instead of that you should use join.
Don't use sub-queries in select statement instead use proper join on Mktg_Unit_Booking in after from statement.
you query should something look like :
select
sys_tree.TreeName AS PhaseName,
case
WHEN Mktg_Unit_Booking.ProductType = 'U' then prop_unit.UnitNo
else prop_car_park.BayNo
end as UnitNo,
mktg_customer.CustomerName AS CustomerName
FROM Mktg_Unit_Booking
left join sys_tree on sys_tree.TreeId = Mktg_Unit_Booking.ProjectLevelId
left join prop_unit on prop_unit.UnitId = Mktg_Unit_Booking.UnitId
left join prop_car_park on prop_car_park.CarParkId = Mktg_Unit_Booking.UnitId
left join mktg_customer on mktg_customer.CustomerId = Mktg_Unit_Booking.CustomerId
WHERE IsDeleted <> '1' AND IsApproved = '1'
AND UnitId = 1110 AND ProductType = 'U'
ORDER BY UnitNo;
I have assumed that each table consists of only 1 matching tuple. If there are more then your logic needs to be modified.

How to use MYSQL's "AS" returned value inside WHERE clause?

I have a query like below...
SELECT
contents.id, contents.title, contents.createdBy,
(SELECT userGroup FROM suser_profile WHERE userId =
(SELECT users.id
FROM
users
WHERE
login = contents.createdBy)
) as userGroupID
FROM
contents
WHERE
contents.id > 0
AND contents.contentType = 'News'
**AND userGroupID = 3**
LIMIT 0, 10
When I try to assign the userGroupID inside WHERE clause the SQL fires an error saying SQL Error(1054):Unknown column "userGroupID" in "where clause"
meantime, if I make little changes like below,,
SELECT
contents.id, contents.title, contents.createdBy
FROM
smart_cms_contents
WHERE
contents.id > 0
AND contents.contentType = 'News'
**AND (SELECT userGroup FROM user_profile WHERE userId =
(SELECT users.id
FROM
users
WHERE
users.login = contents.createdBy)
) = 3**
LIMIT 0, 10
then the query works fine.
I have to use multiple userGroupID checking so that, 2nd style will make the query big, I have to have an style like first one, any help appreciated.
*NOTE : Table names are not original name what I am using in my project. You may ignore it if there are mistakes in table name. My main concern is on using the values assign to a variable by AS inside the WHERE clause.
Ignore the STARS in query*
use HAVING. example:
WHERE
contents.id > 0
AND
contents.contentType = 'News'
HAVING
userGroupID = 3
LIMIT 0, 10
If I'm understanding your initial query properly, then I believe what you want to do is a join:
SELECT DISTINCT
contents.id, contents.title, contents.createdBy
FROM
contents INNER JOIN users
ON contents.createdBy = users.login
INNER JOIN user_profile
ON user_profile.userId = users.id
WHERE
contents.id > 0
AND contents.contentType = 'News'
AND user_profile.userGroupID = 3
LIMIT 0, 10
Totally not the answer to the question you asked, but...
SELECT DISTINCT c.id, c.title, c.createdBy, s.userGroup AS userGroupID
FROM contents AS c
INNER JOIN users AS u
ON c.createdBy = u.login
INNER JOIN suser_profile AS s
ON s.userId = u.id
WHERE c.id > 0
AND c.contentType = 'News'
AND s.userGroup = 3