mysql where condition priority - mysql

I have the following query and it gets the first result "Edit: of each contact" as I want but I do not need any other results per contact after. So example if they have 3 phone # and one is main, it gives me the main then the other 2. I have tried grouping but it ends up getting the first one pulled no matter if main or not. Any thoughts to how I could do this without doing a php check in the loop for if contact already exists?
SELECT pc.firstname, pc.lastname, pp.areacode, pp.prefix, pp.last4
, if(pc.mainphone = pp.phoneid,1,0) as phone_priority
FROM contacts pc
JOIN phone pp ON ( (pp.contid = pc.contid && pp.phoneid = pc.mainphone) || (pp.contid = pc.contid) )
ORDER BY pc.lastname ASC, pc.firstname ASC, phone_priority DESC
Table setup:
contacts (id, firstname, lastname, mainphone)
phone (id, areacode, prefix, last4)
Using MySQL 4.

Use a LIMIT expression to narrow the result set down to the first result. See SELECT Syntax for details.

Use LIMIT 1 at the end of your query

Use a left join:
SELECT pc.firstname,
pc.lastname,
pp.areacode,
pp.prefix,
pp.last4 ,
CASE
WHEN pp.mainphone IS NULL THEN 0
ELSE 1
END AS phone_priority
FROM contacts pc LEFT JOIN phone pp
ON pp.contid = pc.contid
AND pp.phoneid = pc.mainphone
ORDER BY pc.lastname ASC, pc.firstname ASC, phone_priority DESC

Try this variant -
SELECT t.contid
, t.firstname
, t.lastname
, if(t.phoneid IS NULL, p2.phoneid, t.phoneid) phone
, if(t.phoneid IS NULL, p2.areacode, t.areacode) areacode
, if(t.phoneid IS NULL, p2.prefix, t.prefix) prefix
, if(t.phoneid IS NULL, p2.last4, t.last4) last4
FROM
(
SELECT c.contid
, c.firstname
, c.lastname
, c.mainphone
, p.phoneid
, p.areacode
, p.prefix
, p.last4
FROM
contacts c
LEFT JOIN phone p
ON c.contid = p.contid AND c.mainphone = p.phoneid) t
LEFT JOIN phone p2
ON t.contid = p2.contid AND t.mainphone <> p2.phoneid
GROUP BY
t.contid

Related

I want to improve the speed of query statements, but I want to know how

When executing a query statement, the speed is very slow.
SELECT
T1.APPL_SEQ
, T1.COMP_CD
, (SELECT COMP_NM FROM tb_company WHERE COMP_CD = T1.COMP_CD) AS COMP_NM
, T1.GPROD_CD
, (SELECT GPROD_NM FROM tb_gprod WHERE GPROD_CD = T1.GPROD_CD) AS GPROD_NM
, T1.SITE_CD
, (SELECT SITE_NM FROM tb_site WHERE SITE_CD = T1.SITE_CD) AS SITE_NM
, T1.INFLOW_CD
, T1.INFLOW_URL
, T1.STATUS
, T1.REG_DTM
, DECRYPTO(T1.NAME) AS NAME
, DECRYPTO(T1.HP) AS HP
, ifnull(T1.AGE,T1.`115`) AS AGE
, ifnull(T1.GENDER,T1.`116`) AS GENDER
, ifnull(T1.MEMO,T1.`120`) AS MEMO
, ifnull(T1.`105`,T1.`124`) AS TIME
, T1.`125` AS AGE_CHILD
, T2.API_YN
, T2.API_START_DT
, T2.API_END_DT
, T2.API_CD
, T2.DATA_INFLOWCD
, T2.CONFIRM_YN
, T2.SALE_YN
, T2.SALE_PRICE
, T2.BREAKDOWN
, T2.INPUT_DATE
, T3.DIST_YN
, T3.DIST_DT
,(select ifnull((select timestampdiff(DAY, T11.REG_DTM,T1.REG_DTM) AS DIFF2REGTIME from tb_applicant T11 WHERE T11.HP = T1.HP AND T11.GPROD_CD = T1.GPROD_CD AND T11.REG_DTM < T1.REG_DTM order by T11.REG_DTM desc limit 1),-1)) AS HP2_COUNT
FROM
tb_applicant T1
LEFT JOIN mm_applicant T2
ON T1.APPL_SEQ = T2.APPL_SEQ
LEFT JOIN dist_applicant T3
ON T1.APPL_SEQ = T3.APPL_SEQ
LEFT JOIN tb_site T4
ON T4.site_cd = T1.SITE_CD and T4.comp_cd = T1.COMP_CD and T4.gprod_cd = T1.GPROD_CD
WHERE 1=1
AND T1.APPL_SEQ > 147293
AND T4.is_use = 'Y'
$Sql_Search
ORDER BY
$Sql_OrderBy
) U1
, (SELECT #ROWNUM := 0) U2
) V1";
,(select ifnull((...),-1)) AS HP2_COUNT
This is part of why it's so slow.
This query calculates the number of months difference by comparing REG_DTM when the td_applicant table has the same data for HP, GPROD, and COMP.
I don't need to get the date difference, is there any way to improve the query speed?
The main problem are those subselect in the select. As #Akina suggested, you should move them in FROM and make them as join.
They way you have done implies that each subselect is executed for each row returned by the main select.
You have 4 subselect that mean if you have 100 rows you execute 1 (main select) + (4*100) query so 401 instead of 1.
Using join allow the internal optimization engine to choose the best strategy to perform the query, in your way practically no optimization are applied.
I post a short example of how should be your query, didn't refactor the whole query since without database is a bit difficult to do it and I can easily produce a wrong query.
Notice that you select twice on tb_site with different condition, so is up to you to put the correct one.
SELECT T1.APPL_SEQ, T1.COMP_CD, T1.GPROD_CD, T1.SITE_CD
TC.COMP_NM,
TG.GPROD_NM,
TS.SITE_NM,
......
FROM tb_applicant T1
LEFT JOIN mm_applicant T2
JOIN tb_company TC on TC.COMP_CD = T1.COMP_CD
JOIN tb_gprod TG on GPROD_CD = T1.GPROD_CD
JOIN tb_site TS on TS.SITE_CD = T1.SITE_CD ON T1.APPL_SEQ = T2.APPL_SEQ
.......

How to use the SELECT clause twice in a sql statement

I want to have a table where I can view today's balance as well as yesterday's balance as two different column. Is there any way I can select from two different dates?
Below is the SQL statement I have tried however I am not able to see the yesterday balance.
(SELECT food.food_id, food.food_name, food.food_chi_name, food.food_category, food.chinesechar, SUM(inventory.tmr_input+inventory.final_balance), SUM(inventory.balance), SUM(inventory.input), SUM(inventory.reject), SUM(inventory.final_balance), SUM(inventory.tmr_input), SUM(inventory.sale), SUM(inventory.theoritical), SUM(inventory.yest_theoritical), SUM(inventory.3PMsale), SUM(inventory.3PMbalance), SUM(inventory.wholesale) FROM inventory INNER JOIN food ON inventory.food_id=food.food_id WHERE food.outlet = 'T11' AND inventory.date = '04/30/2021' GROUP BY food.food_id ORDER BY food.food_id ASC); (SELECT SUM(inventory.balance) as yesterday_balance FROM inventory INNER JOIN food ON inventory.food_id=food.food_id WHERE food.outlet = 'T11' AND inventory.date = '04/29/2021' GROUP BY food.food_id ORDER BY food.food_id ASC);
You can use a subselect for that purpose
And using aliases helps to get a better overview
SELECT
f1.food_id
, f1.food_name
, f1.food_chi_name
, f1.food_category
, f1.chinesechar
, SUM(i1.tmr_input+i1.final_balance)
, SUM(i1.balance)
, SUM(i1.input)
, SUM(i1.reject)
,SUM(i1.final_balance)
, SUM(i1.tmr_input)
, SUM(i1.sale)
, SUM(i1.theoritical)
, SUM(i1.yest_theoritical)
, SUM(i1.`3PMsale`)
, SUM(i1.`3PMbalance`)
, SUM(i1.wholesale)
,(SELECT SUM(i2.balance)
FROM
inventory i2 INNER JOIN food f2 ON i2.food_id=f2.food_id
WHERE f2.outlet = 'T11' AND i2.date = '04/29/2021'
AND f2.food_id = f1.food_id
) as yesterday_balance
FROM
inventory i1
INNER JOIN
food f1 ON i1.food_id=f1.food_id
WHERE
f1.outlet = 'T11'
AND i1.date = '04/30/2021'
GROUP BY f1.food_id
ORDER BY f1.food_id ASC;

MySQL Join returns too many rows with WHERE clause

I am having trouble with a SQL query. So in my project user can reserve a ride. I want to display reserved rides by users ID (passenger_id) but query returns all users (driver_id) advertisements when user reserved a ride only for one of drivers advertisements.
SELECT advertisement.id
, COUNT(review.driver_id) AS 'review_count'
, ROUND(AVG(review.mark) ,1) AS 'rating'
, users.unique_id
, users.name
, users.surname
, users.phone
, YEAR(CURDATE()) - YEAR(users.birthdate) AS age
, users.image
, advertisement.from_city
, advertisement.to_city
, users.car_name
, users.car_model
, users.car_make_year
, advertisement.number_of_places
, advertisement.price
, advertisement.datetime
, advertisement.info
FROM reserved_rides
JOIN advertisement
ON reserved_rides.driver_id = advertisement.user_id
LEFT
JOIN review
ON reserved_rides.driver_id = review.driver_id
JOIN users
ON reserved_rides.driver_id = users.unique_id
WHERE reserved_rides.passenger_id = ?
GROUP
BY advertisement.id
ORDER
BY advertisement.datetime ASC
What is going wrong here?
I hope replacing GROUP BY advertisement.id with GROUP BY reserved_rides.driver_idsolves your problem. cheers

How to fetch data by a conditional query(left join/right join/inner join/ multi select, ... or) from multi table

I have a problem with conditional query and can't solve that and I need your help.
I use mysql with innodb and I have some tables like bellow.
table1:
---------------------- uers_type_1 ----------------------
user_id int(11) primary auto_increment
firstname varchar(15)
lastname varchar(20)
.
.
.
table 2:
---------------------- uers_type_2 ----------------------
user_id int(11) primary auto_increment
firstname varchar(15)
lastname varchar(20)
.
.
.
table 3:
---------------------- user_request ----------------------
request_id int(11) primary auto_increment
user_type enum("ut1","ut2")
user_id int(11)
request text
Now I need a query to fetch something like this:
user request data with user first name and last name from users type tables
Result:
-----------------------------------------------
- firstName - lastName - request_id - request -
-----------------------------------------------
- Robert - De Niro - 10 - some request.
- Will - Smith - 93 - some request.
.
.
.
My query something like this but it's not work
SELECT r.request_id , r.request, (
execute(
concat('select u.first_name AS firstName, u.lastname AS lastName from ',
(SELECT CASE r.user_type WHEN 'ut1'
THEN 'uers_type_1' WHEN 'ut2' THEN 'uers_type_2' END),
' u WHERE u.user_id = ', r.user_id, ''
)
)
)
FROM `user_request` r
WHERE 1
I got a solution for your problem,
select user_request.r_id , user_request.request,
(select Case user_request.user_type when 'u1' then user_type_1.fistname else user_type_2.firstname end ) as name
from user_request
left JOIN user_type_1 on user_request.user_id = user_type_1.id and user_request.user_type = 'u1'
left JOIN user_type_2 on user_request.user_id = user_type_2.id and user_request.user_type = 'u2'
the point is that :
try to point both tables in the query using left join and then in your select options choose where it should be used for fetching the data.
(select Case user_request.user_type when 'u1' then user_type_1.firstname else user_type_2.firstname end ) as name,
(select Case user_request.user_type when 'u1' then user_type_1.lastname else user_type_2.lastname end ) as family
hope that help you.
Having 2 user tables is going to perplex and frustrate you. While I don't know why you have them if it is possible consider moving them into to a single table. In any case, you can produce a single view of the user information by using a UNION ALL query such as this:
SELECT
'ut1' AS user_type
, user_id
, firstname
, lastname
FROM user_type_1
UNION ALL
SELECT
'ut2' AS user_type
, user_id
, firstname
, lastname
FROM user_type_2
;
That query might literally be used to create a view but that it is optional. That UNION ALL approach can be used as a derived table subquery in a query like the following. Notice how this simplifies access to the name columns.
SELECT
user_request.r_id
, user_request.request
, u.firstname ## simple to access
, u.lastname ## simple to access
FROM user_request AS ur
INNER JOIN (
SELECT
'ut1' AS user_type
, user_id
, firstname
, lastname
FROM user_type_1
UNION ALL
SELECT
'ut2' AS user_type
, user_id
, firstname
, lastname
FROM user_type_2
) AS u ON ur.user_type = u.user_type
AND ur.user_id = u.user_id
;
Another alternative is to use 2 left joins, one for each user table and include the user_type into each join as a condition of the join. Note here the names can be NULL due the way the left joins will work, so you can overcome that by seing COALESCE() or IFNULL()
SELECT
ur.r_id
, ur.request
, COALESCE(ut1.fistname,ut2.firstname) firstname
, COALESCE(ut1.lastname,ut2.lastname) lastname
FROM user_request as ur
LEFT JOIN user_type_1 as ut1 ON ur.user_id = ut1.id
AND ur.user_type = 'ut1'
LEFT JOIN user_type_2 as ut2 ON ur.user_id = ut2.id
AND ur.user_type = 'ut2'
;
If you should choose to create a view, e.g.
CREATE OR REPLACE VIEW users_all_v
AS
SELECT
'ut1' AS user_type
, user_id
, firstname
, lastname
FROM user_type_1
UNION ALL
SELECT
'ut2' AS user_type
, user_id
, firstname
, lastname
FROM user_type_2
;
Then subsequent queries become easier to assemble e.g.
SELECT
user_request.r_id
, user_request.request
, u.firstname ## simple to access
, u.lastname ## simple to access
FROM user_request AS ur
INNER JOIN users_all_v AS u ON ur.user_type = u.user_type
AND ur.user_id = u.user_id
;
AND even if you do one day combine those 2 user tables into 1, you can just adjust that view and existing queries will not be broken.

Select the row for the most recent day

I am trying to achieve the comment (title) written on the most recent day (review_date) from each user (username) in the database
My code is :
select tb1.*, tb2.* from
(select username, via_mobile as pc, max(review_date) as pcdate from tripadvisor where username != "" and via_mobile='false' group by username) tb1
join
(select username, via_mobile as mobile, max(review_date) as mobile from tripadvisor whereusername != "" and via_mobile='true' group by username) tb2
on tb1.username = tb2.username;
The problem is I cannot get the right review for the right date.
For example :
username; review_date; title
Alan; 2012-12-18 ;"If..."
But it should display Alan; 2012-12-18; "Nice hotel"
Can you help me to fix the code.
Your question is a little unclear, but if I understand correctly, you're looking for each full row with the highest date, selected distinctly/grouped by username? This should give you that:
SELECT
username,
review_date,
title
FROM
tripadvisor AS ta
INNER JOIN (
SELECT
username,
max(review_date) AS review_date
FROM
tripadvisor
GROUP BY
username
) AS max_table
ON ta.username = max_table.username
AND ta.review_date = max_table.review_date;
WHERE
username != ''
-- any extra where clauses go here
See: How can I SELECT rows with MAX(Column value), DISTINCT by another column in SQL?
If the goal is to return the most recent review title for "mobile" and for "pc", I'd do something like this:
SELECT q.username
, MAX(q.pc_date) AS pc_date
, MAX(p.title) AS pc_title
, MAX(q.mobile_date) AS mobile_date
, MAX(r.title) AS mobile_title
FROM ( SELECT t.username
, MAX(IF(t.via_mobile='false',t.review_date,NULL) AS pc_date
, MAX(IF(t.via_mobile='true',t.review_date,NULL) AS mobile_date
FROM tripadvisor t
WHERE t.username <> ''
AND t.via_mobile IN ('true','false')
GROUP
BY t.username
) q
LEFT
JOIN tripadvisor p
ON p.username = q.username
AND p.review_date = q.pc_date
AND p.via_mobile = 'false'
LEFT
JOIN tripadvisor r
ON r.username = q.username
AND r.review_date = q.mobile_date
AND r.via_mobile = 'true'
GROUP
BY q.username
If the user has only "mobile" reviews and no "pc" reviews, this query will return a row, but with NULL values for the "pc" columns. Similarly, the query will return NULL values for the "mobile" columns for a user that has only "pc" reviews.
The query could easily be changed to only returns rows for users that have both "mobile" and "pc" reviews, to be closer to the original using the INNER JOIN.
If the goal is simpler, just to return just the most recent review...
SELECT r.username
, r.review_date
, MAX(r.title) AS title
, MAX(r.via_mobile) AS via_mobile
FROM ( SELECT t.username
, MAX(t.review_date) AS max_review_date
FROM tripadvisor t
WHERE t.username <> ''
AND t.via_mobile IN ('true','false')
GROUP
BY t.username
) q
JOIN tripadvisor r
ON r.username = q.username
AND r.review_date = q.max_review_date
AND r.via_mobile IN ('true','false')
GROUP
BY r.username
, r.review_date
The results of this query are somewhat indeterminate when a username has multiple rows with identical (most recent) review_date. This guarantees a single row will be returned, but the title and via_mobile may not be from the same row.
Your question could be expressed as an existence test: show the rows for which the review date matches the latest review date for that user.
Existence is tested with EXISTS in a correlated subquery:
SELECT * FROM tripadvisor as T
where exists (
select 1 from tripadvisor
where username = T.username
group by username
having MAX(review_date) = T.review_date
);