Select in Select MySQL - mysql

I have two queries, i'd like if is possible execute in only one query as a Select in Select.
The first one:
SELECT
users.id
FROM users
LEFT JOIN users_date ON users_date.user = users.id
LEFT JOIN users_varchar ON users_varchar.user = users.id
WHERE
abilitato = 1
AND users_date.key = 'birthday'
AND users_varchar.key = 'nation'
AND users_varchar.value = 'US'
AND (users.reg_date >= '2013-05-31' AND users.reg_date <= '2013-05-31')
AND (floor(DATEDIFF(NOW(), users_date.value) / 365) >= 19 AND floor(DATEDIFF(NOW(), users_date.value) / 365) <= 19)
it retrieve a list of user id (filtered by Age, Nation or Date of registration)
the second one:
SELECT count(`matches`.`id`) FROM `matches` WHERE (`matches`.`status_home` = 3 AND `matches`.`status_guest` = 3) AND (`matches`.`team_home` = 13 OR `matches`.`team_guest` = 13)
i need perform the second select for every ID retrieved by the one's.
for every perform i must replace the value 13 with the id retrieved.
it is possible perform all in a single query with a select in select?
thanks advance for your help

Try this sql.
SELECT count(`matches`.`id`)
FROM `matches` m
INNER JOIN ( SELECT users.id as id
FROM users
LEFT JOIN users_date ON users_date.user = users.id
LEFT JOIN users_varchar ON users_varchar.user = users.id
WHERE abilitato = 1
AND users_date.key = 'birthday'
AND users_varchar.key = 'nation'
AND users_varchar.value = 'US'
AND (users.reg_date >= '2013-05-31' AND users.reg_date <= '2013-05-31')
AND (floor(DATEDIFF(NOW(), users_date.value) / 365) >= 19 AND floor(DATEDIFF(NOW(), users_date.value) / 365) <= 19)) t
on t.id=m.team_guest
WHERE (`matches`.`status_home` = 3 AND `matches`.`status_guest` = 3)
AND (`matches`.`team_home` = 13 OR `matches`.`team_guest` = t.id)
GROUP BY t.id

You can express what you want to do as an explicit join. But the question asks for a select within a select. For that, you need a correlated subquery. Here is the final query:
select users.id,
(SELECT count(`matches`.`id`)
FROM `matches`
WHERE (`matches`.`status_home` = 3 AND `matches`.`status_guest` = 3) AND
(`matches`.`team_home` = user.id OR `matches`.`team_guest` = users.id)
) as cnt
FROM users LEFT JOIN
users_date
ON users_date.user = users.id LEFT JOIN
users_varchar
ON users_varchar.user = users.id
WHERE abilitato = 1 AND users_date.key = 'birthday' AND
users_varchar.key = 'nation' AND users_varchar.value = 'US' AND
(users.reg_date >= '2013-05-31' AND users.reg_date <= '2013-05-31') AND
(floor(DATEDIFF(NOW(), users_date.value) / 365) >= 19 AND
floor(DATEDIFF(NOW(), users_date.value) / 365) <= 19
)
(The formatting helps me understand the query better.)
Doing this without a correlated subquery is a bit challenging because of the or in the matching condition.

Related

mysql select if datediff is greater than

I would like to query users from lat 3 years only if the time diffrence of date_start and date_end is greater than 3 months. I have tried a lot of things but nothing works. This is the statment i made until now :
SELECT accounts.account_id, accounts.name, accounts.active_club_id,
accounts.phone, MIN(shifts_accounts.date_start) as 'datestart', MAX(shifts_accounts.date_end) as 'dateend'
FROM `shifts_accounts`
JOIN accounts ON shifts_accounts.account_id = accounts.account_id
JOIN accounts_groups ON accounts.account_id = accounts_groups.account_id
WHERE accounts_groups.group_id = 7
AND DATEDIFF('dateend', 'datestart') > 90
AND accounts.active_club_id != 1
AND shifts_accounts.date_start > '2014-11-28'
AND shifts_accounts.date_start < '2017-11-28' GROUP BY accounts.account_id
The following is an example of a valid query. I don't think there's enough information to say whether it's what you want or not...
SELECT a.account_id
, a.name
, a.active_club_id
, a.phone
, MIN(sa.date_start) datestart
, MAX(sa.date_end) dateend
FROM shifts_accounts sa
JOIN accounts a
ON a.account_id = sa.account_id
JOIN accounts_groups ag
ON ag.account_id = a.account_id
WHERE ag.group_id = 7
AND a.active_club_id != 1
AND sa.date_start BETWEEN '2014-11-28' AND '2017-11-28'
GROUP
BY a.account_id
HAVING DATEDIFF(dateend, datestart) > 90;
For further help, see: Why should I provide an MCVE for what seems to me to be a very simple SQL query?
Try with this,
SELECT accounts.account_id, accounts.name, accounts.active_club_id,
accounts.phone, MIN(shifts_accounts.date_start) as 'datestart', MAX(shifts_accounts.date_end) as 'dateend'
FROM `shifts_accounts`
JOIN accounts ON shifts_accounts.account_id = accounts.account_id
JOIN accounts_groups ON accounts.account_id = accounts_groups.account_id
WHERE accounts_groups.group_id = 7
AND DATEDIFF('dateend', 'datestart') > 90
AND accounts.active_club_id != 1
AND shifts_accounts.date_start < DATE_SUB(NOW(),INTERVAL 3 YEAR) GROUP BY accounts.account_id

Buyers structure by registration date query optimisation

I would like to show buyers structure by their registration date e.g.:
H12016 10.000 buyers
from which
2.000 registered in H12014
4.000 registered in H22014
etc.
I have two queries for that:
Number 1 (buyers from H12016 (about 50k records)):
SELECT DISTINCT
r.idUsera as id_usera
FROM
rezerwacje r
WHERE
r.dataZalozenia between '2016-01-01' and '2016-07-01'
and r.`status` = 'zabookowana'
ORDER BY
id_usera
Number 2 (users_ids and their registration (insert) date (about 3,8M users)):
SELECT
m.user_id,
date(m.action_date) as data_insert
FROM
mwids m
WHERE
m.`type` = 'insert'
Both queries separately run fine, but when I try to combine them like so:
SELECT DISTINCT
r.idUsera as id_usera,
t1.data_insert
FROM
rezerwacje r
LEFT JOIN
(
SELECT
m.user_id,
date(m.action_date) as data_insert
FROM
mwids m
WHERE
m.`type` = 'insert'
) t1 ON t1.user_id = r.idUsera
WHERE
r.dataZalozenia between '2016-01-01' and '2016-07-01'
and r.`status` = 'zabookowana'
ORDER BY
id_usera
this query runs "indefinetely" and I have to kill it after some time.
I do not belive it should run that long. If the query Number 2 was smaller i.e. about 1M users I could combine results in Excel in matter of seconds. So why is it not possible inside the database? What am I doing wrong?
SELECT DISTINCT
r.idUsera as id_usera,
t1.data_insert
FROM
rezerwacje r
INNER JOIN
(
SELECT
m.user_id,
date(m.action_date) as data_insert
FROM
mwids m
WHERE
m.`type` = 'insert'
) t1 ON t1.user_id = r.idUsera
WHERE
r.dataZalozenia between '2016-01-01' and '2016-07-01'
and r.`status` = 'zabookowana'
ORDER BY
id_usera
Try with INNER JOIN.
Query 1 needs
INDEX(status, dataZalozenia, id_usera)
Query 3: Rewrite thus:
If there is only one row in mwids for 'insert' per user:
SELECT r.idUsera as id_usera, DATE(m.action_date) AS data_insert
FROM rezerwacje r
LEFT JOIN mwids m ON m.user_id = r.idUsera
AND m.`type` = 'insert'
WHERE r.dataZalozenia >= '2016-01-01'
AND r.dataZalozenia < '2016-01-01' + 12 MONTH
and r.`status` = 'zabookowana'
ORDER BY r.idUsera
with
INDEX(status, dataZalozenia, isUsera) -- on r
INDEX(type, user_id, action_date) -- on m
If there can be multiple rows, do this:
SELECT r.idUsera as id_usera,
( SELECT DATE(m.action_date)
FROM mwids m
WHERE m.user_id = r.idUsera
AND m.`type` = 'insert'
LIMIT 1
) AS data_insert
FROM rezerwacje r
LEFT JOIN mwids m ON m.user_id = r.idUsera
AND m.`type` = 'insert'
WHERE r.dataZalozenia >= '2016-01-01'
AND r.dataZalozenia < '2016-01-01' + 12 MONTH
and r.`status` = 'zabookowana'
ORDER BY r.idUsera
But you will be getting a random action_date. So maybe you want MIN() or MAX()?

Using two different measures in a having statement

I am including the code I am trying to use. What I want is to get a count of encounter id's for each doctor. I want to capture patients who have had at least 2 visits or who have had one preventive visit. I don't know how to make this work using the having statement. thanks for any assistance.
SELECT e.doctorID, COUNT(DISTINCT e.encounterID) AS VisitCount
FROM enc e
JOIN users u ON e.patientID = u.uid
Left JOIN diagnosis d ON e.encounterID = d.encounterID
LEFT JOIN items it ON d.itemID = it.itemID
LEFT JOIN itemdetail id ON it.itemID = id.itemID
WHERE e.encType = 1 AND e.status = 'CHK' AND e.deleteFlag = 0 AND
e.date BETWEEN DATE_ADD((LAST_DAY(DATE_ADD(CURDATE(),INTERVAL -2 MONTH))), INTERVAL 1 DAY) AND LAST_DAY(DATE_ADD(CURDATE(),INTERVAL -1 MONTH))
AND FLOOR(DATEDIFF(NOW(),u.ptdob)/365.25) >= 18 AND e.doctorID = e.resourceID
GROUP BY e.doctorID, id.value
HAVING
COUNT(e.patientid)>=2 OR
id.value in ('Z00.00', 'Z00.01')
You dont need to include id.value in grp by clause. Try to give valid condition in having clause with id.value
select e.doctorID,
COUNT(distinct e.encounterID) as VisitCount from enc e join users u on e.patientID = u.uid
left join diagnosis d on e.encounterID = d.encounterID left join items it on
d.itemID = it.itemID left join itemdetail id on
it.itemID = id.itemID where e.encType = 1 and e.status ='CHK' and e.deleteFlag = 0 and
e.date between DATE_ADD((LAST_DAY(DATE_ADD(CURDATE(), INTERVAL - 2 MONTH))), INTERVAL 1 DAY)
and LAST_DAY(DATE_ADD(CURDATE(), INTERVAL - 1 MONTH))
and
FLOOR(DATEDIFF(NOW(), u.ptdob) / 365.25) >= 18
and e.doctorID = e.resourceID group by e.doctorID
having COUNT(e.patientid) >= 2
or sum(case when id.value in ('Z00.00','Z00.01') then 1
else 0 end)>=1

How to optimize for speed a sql multiple select with SUM

I have a really long select from my database with many joins. The problem is with counting SUM: without sum, select time is about 3s, but with SUM is about 15s.
Is it possible to optimize my select to obtain a shorter select time?
Here is my code:
SELECT
accomodation.id,
accomodation.aid,
accomodation.title_en,
accomodation.title_url_en,
accomodation.address,
accomodation.zip,
accomodation.stars,
accomodation.picture,
accomodation.valid_from,
accomodation.valid_to,
accomodation.latitude,
accomodation.longitude,
accomodation.city_id AS
accomodation_city_id,
db_cities.id AS city_id,
db_cities.title_en AS city,
db_cities.title_url AS city_url,
db_countries.title_en AS country_title,
db_countries.title_url_en AS country_url,
accomodation_type.class AS accomodation_type_class,
accomodation_review_value_total.value AS review_total,
MIN(accomodation_price.price) AS price_from,
accomodation_rooms.total_persons
FROM
(SELECT aid, MAX(info_date_add) AS max_info_date_add FROM accomodation GROUP BY aid) accomodation_max
INNER JOIN accomodation
ON
accomodation_max.aid = accomodation.aid AND
accomodation_max.max_info_date_add = accomodation.info_date_add
LEFT JOIN db_cities
ON (
db_cities.id = accomodation.city_id OR
(((acos(sin((db_cities.latitude*pi()/180)) * sin((accomodation.latitude*pi()/180)) + cos((db_cities.latitude*pi()/180)) * cos((accomodation.latitude*pi()/180)) * cos(((db_cities.longitude - accomodation.longitude)*pi()/180))))*180/pi())*60*1.1515*1.609344) < '20')
JOIN db_countries
ON db_countries.id = accomodation.country_id
LEFT JOIN accomodation_review_value_total
ON accomodation_review_value_total.accomodation_aid = accomodation.aid
LEFT JOIN accomodation_type_value
ON accomodation_type_value.accomodation_id = accomodation.id
LEFT JOIN accomodation_type
ON accomodation_type.id = accomodation_type_value.accomodation_type_id
JOIN accomodation_season
ON (
accomodation_season.accomodation_aid = accomodation.aid AND
( '2013-11-04' BETWEEN accomodation_season.start_date AND accomodation_season.end_date OR '2013-11-05' BETWEEN accomodation_season.start_date AND accomodation_season.end_date ) )
JOIN accomodation_price
ON
accomodation_price.accomodation_aid = accomodation.aid AND
accomodation_price.accomodation_price_type_id = '1' AND
accomodation_price.accomodation_price_cat_id = '1' AND
accomodation_price.price BETWEEN '20' AND '250' AND
accomodation_price.accomodation_season_id = accomodation_season.id
JOIN accomodation_theme_value
ON accomodation_theme_value.accomodation_id = accomodation.id
INNER JOIN
(SELECT
accomodation_id,
SUM(accomodation_rooms.rooms) AS total_rooms,
SUM(accomodation_rooms.beds * accomodation_rooms.rooms) AS total_persons
FROM accomodation_rooms
GROUP BY accomodation_id) accomodation_rooms
ON
accomodation_rooms.accomodation_id = accomodation.id AND
accomodation_rooms.total_persons >= '4'
WHERE
db_countries.title_url_en LIKE '%spain%' AND
db_cities.title_url LIKE '%barcelona%' AND
accomodation_type_value.accomodation_type_id IN (5,10) AND
total_rooms >= '2' AND
accomodation_theme_value.accomodation_theme_id IN (11,12,13) AND
accomodation.stars IN (3,4,5) AND
( accomodation_review_value_total.value >= '4.5' ) AND
db_cities.id = '2416'
GROUP BY accomodation.aid
ORDER BY
CASE
WHEN accomodation.valid_to>=NOW() AND accomodation.valid_from<=NOW() AND MIN(accomodation_price.price) IS NOT NULL THEN 0
WHEN NOW()>accomodation.valid_to AND accomodation.valid_to>'0000-00-00' AND MIN(accomodation_price.price) IS NOT NULL THEN 1
WHEN accomodation.valid_to>=NOW() AND accomodation.valid_from<=NOW() THEN 2
WHEN NOW()>accomodation.valid_to AND accomodation.valid_to>'0000-00-00' THEN 3
ELSE 4 END,
review_total DESC,
accomodation.title_en
LIMIT 10

how to group by with a sql that has multiple subqueries

I am trying to add a group by SC_FRAMES.GROUPPRODUCTTYPE to this statement:
SELECT
SC_JOBS.CREATIONDATE,
(SELECT SUM(SC_JOBS.GROSSEXCLVAT) FROM SC_JOBS WHERE SC_FRAMES.GROUPPRODUCTTYPE = 'ABC' AND SC_FRAMES.JOBID = SC_JOBS.JOBSID AND SC_JOBS.INVOICEDATE < '1990-01-01') AS Product1,
(SELECT SUM(SC_JOBS.GROSSEXCLVAT) FROM SC_JOBS WHERE SC_FRAMES.GROUPPRODUCTTYPE = 'XYZ' AND SC_FRAMES.JOBID = SC_JOBS.JOBSID AND SC_JOBS.INVOICEDATE < '1990-01-01') AS Product2
FROM
SC_JOBS
INNER JOIN
SC_FRAMES ON SC_FRAMES.JOBID = SC_JOBS.JOBSID
WHERE
SC_JOBS.CREATIONDATE BETWEEN :StartDate AND :EndDate
ORDER BY
SC_JOBS.CREATIONDATE
Any suggestions please?
I think you want a query like this:
SELECT f.GROUPPRODUCTTYPE,
MIN(j.CREATIONDATE),
SUM(case when j.INVOICEDATE < '1990-01-01' then j.GROSSEXCLVAT else 0
end) as Product1
FROM SC_JOBS INNER j JOIN
SC_FRAMES f
ON f.JOBID = j.JOBSID
WHERE j.CREATIONDATE BETWEEN :StartDate AND :EndDate
GROUP BY f.GROUPPRODUCTTYPE
ORDER BY min(j.CREATIONDATE);
It replaces the subqueries with conditional aggregation, based on the invoice date.
Try this:
SELECT
SC_JOBS.CREATIONDATE,
(SELECT SUM(SC_JOBS.GROSSEXCLVAT) FROM SC_JOBS WHERE SC_FRAMES.GROUPPRODUCTTYPE = 'ATI' AND SC_FRAMES.JOBID = SC_JOBS.JOBSID AND SC_JOBS.INVOICEDATE < '1990-01-01') AS Product 1,
(SELECT SUM(SC_JOBS.GROSSEXCLVAT) FROM SC_JOBS WHERE SC_FRAMES.GROUPPRODUCTTYPE = 'ATI' AND SC_FRAMES.JOBID = SC_JOBS.JOBSID AND SC_JOBS.INVOICEDATE < '1990-01-01') AS Product 2
FROM
SC_JOBS
INNER JOIN
SC_FRAMES ON SC_FRAMES.JOBID = SC_JOBS.JOBSID
WHERE
SC_JOBS.CREATIONDATE BETWEEN :StartDate AND :EndDate
GROUP BY
SC_FRAMES.GROUPPRODUCTTYPE
ORDER BY
SC_JOBS.CREATIONDATE
Add GROUP BY SC_FRAMES.GROUPPRODUCTTYPE after the end of your WHERE statement and before your ORDER BY statement. Also change your AS values to not include spaces as I've encountered ploblems with this in the past, so use Product1 as opposed to Product 1