Making my mysql query more efficient - mysql

What I am trying to do is pulling the id,phone_type,os_version columns from Enswitch_Mobile_Users table.
And with the id i've just got to get the enswitch_id from Enswitch_Users table.
And after that to COUNT all the entires from Enswitch_Android_Purchases or Enswitch_Iphone_Purchases which the user colum match the id from enswitch_mobile_users. and getting first entry date and the last entry date.
And I managed to made it work with this query:
SELECT p.user AS `Mobile_User_ID`,
e.os_version `Os_Version`,
e.phone_type `Phone_Type`,
eu.enswitch_id `Enswitch_ID`,
Count(1) AS `Buy_Count`,
(SELECT pc.date
FROM
(
SELECT date, user, status
FROM enswitch_android_purchases
UNION
SELECT date, user, status
FROM enswitch_iphone_purchases
) AS pc
WHERE pc.status = 1
AND pc.user = p.user
ORDER BY pc.date ASC
LIMIT 1) AS `First_Purchase`,
(SELECT pc.date
FROM
(
SELECT date, user, status
FROM enswitch_android_purchases
UNION
SELECT date, user, status
FROM enswitch_iphone_purchases
) AS pc
WHERE pc.status = 1
AND pc.user = p.user
ORDER BY pc.date DESC LIMIT 1) AS `Last_Purchase`
FROM
(
SELECT item, date, user, status
FROM enswitch_android_purchases
UNION
SELECT item, date, user, status
FROM enswitch_iphone_purchases
) AS p
LEFT JOIN enswitch_mobile_users e
ON p.user = e.id
LEFT JOIN enswitch_users eu
ON e.user_id = eu.id
WHERE p.`date` >= :from_date
AND p.`date` <= :to_date
AND p.user is not null
AND p.status = 1
GROUP BY `Mobile_User_ID`
But because of the selects it will be really slow so how can I make it more efficient?

You might be able to use the following which replaces the two selects in the SELECT list with min(p.date) and max(p.date):
SELECT p.user AS `Mobile_User_ID`,
e.os_version `Os_Version`,
e.phone_type `Phone_Type`,
eu.enswitch_id `Enswitch_ID`,
Count(1) AS `Buy_Count`,
min(p.date) AS `First_Purchase`,
max(p.date) AS `Last_Purchase`
FROM
(
SELECT item, date, user, status
FROM enswitch_android_purchases
UNION
SELECT item, date, user, status
FROM enswitch_iphone_purchases
) AS p
LEFT JOIN enswitch_mobile_users e
ON p.user = e.id
LEFT JOIN enswitch_users eu
ON e.user_id = eu.id
WHERE p.`date` >= :from_date
AND p.`date` <= :to_date
AND p.user is not null
AND p.status = 1
GROUP BY p.user

Related

I need to get specific ids from db if these are in current and last quarter using SQL

[DB Table]
SELECT b.first_name, b.last_name, a.pod_name, a.category, c.user_id,
SUM(IF(QUARTER(CURDATE())-1 OR (QUARTER(CURDATE())-2) AND a.user_id, 1, 0)) AS flag FROM kudos a
INNER JOIN users b ON a.user_id = b.id INNER JOIN users_groups c ON a.user_id = c.user_id
INNER JOIN groups d ON c.group_id = d.id WHERE a.group_name = 'G2' AND d.id IN (7,8,9,11,12,13,14,15,16,17,21,22,23,24,25,26,27,28)
AND QUARTER(CURDATE())-1 = a.quarter ORDER BY a.final_score+0 DESC
I need to get the user_ids of those users which are both in quarter 1 and 2 from table.
Tried above query but failed to get expected results.
Can someone please guide me on this?
if you only need user_id then you can do this :
select user_id
from tablename
where quarter in (1,2)
group by user_id
having count(distinct quarter) = 2
another way is to use window function, assuming you have one user id in each quarter:
select * from (
select * , count(*) over (partition by user_id) cn
from tablename
where quarter in (1,2)
) t where cn = 2

SQL - Select discint AND count from JOIN query

I have below query:
SELECT u.*
(SELECT sum(trs.amount)
FROM transactions trs
WHERE u.id = trs.user AND trs.type = 'Recycle' AND
trs.TIME >= UNIX_TIMESTAMP(CURDATE())
) as amt
FROM (SELECT DISTINCT user_by
FROM xeon_users_rented
) AS xur JOIN
users u
ON xur.user_by = u.username
LIMIT 50
Which selects some data from my database. The above query works fine. However, I would like to also select count(*) from xeon_users_rented where user_by = u.username This is what I have attempted:
SELECT u.*
(SELECT sum(trs.amount)
FROM transactions trs
WHERE u.id = trs.user AND trs.type = 'Recycle' AND
trs.TIME >= UNIX_TIMESTAMP(CURDATE())
) as amt,
(SELECT DISTINCT count(*)
FROM xeon_users_rented
WHERE xur.user_by = u.username
) AS ttl
FROM (SELECT DISTINCT user_by
FROM xeon_users_rented
) AS xur JOIN
users u
ON xur.user_by = u.username
LIMIT 50
However, that gives me the total number of rows in xeon_users_rented as ttl - not the total distinct rows where username = user_by
I think you can do what you want just by tinkering with your subquery a little. That is, change the select distinct to a group by:
SELECT u.*, xur.cnt,
(SELECT sum(trs.amount)
FROM transactions trs
WHERE u.id = trs.user AND trs.type = 'Recycle' AND
trs.TIME >= UNIX_TIMESTAMP(CURDATE())
) as amt
FROM (SELECT user_by, COUNT(*) as cnt
FROM xeon_users_rented
GROUP BY user_by
) xur JOIN
users u
ON xur.user_by = u.username
LIMIT 50;
Some notes:
SELECT DISTINCT is not really necessary, because you can do the same logic using GROUP BY. So, it is more important to understand GROUP BY.
You are using LIMIT with no ORDER BY. That means that you can get a different set of rows each time you run the query. Bad practice.

how to sort and group by by it's count

I have the following:
SELECT DISTINCT s.username, COUNT( v.id ) AS cnt
FROM `instagram_item_viewer` v
INNER JOIN `instagram_shop_picture` p ON v.item_id = p.id
INNER JOIN `instagram_shop` s ON p.shop_id = s.id
AND s.expirydate IS NULL
AND s.isLocked =0
AND v.created >= '2014-08-01'
GROUP BY (
s.id
)
ORDER BY cnt DESC
Basically I have an instagram_item_viewer with the following structure:
id viewer_id item_id created
It tracks when a user has viewed an item and what time. So basically I wanted to find shops that has the most items viewed. I tried the query above and it executed fine, however it doesn't seem to give the appropriate data, it should have more count than what it is. What am I doing wrong?
First, with a group by statement, you don't need the DISTINCT clause. The grouping takes care of making your records distinct.
You may want to reconsider the order of your tables. Since you are interested in the shops, start there.
Select s.username, count(v.id)
From instagram_shop s
INNER JOIN instagram_shop_picture p ON p.shop_id = s.shop_id
INNER JOIN instagram_item_viewer v ON v.item_id = p.id
AND v.created >= '2014-08-01'
WHERE s.expirydate IS NULL
AND s.isLocked = 0
GROUP BY s.username
Give thata shot.
As mentioned by #Lennart, if you have a sample data it would be helpful. Because otherwise there will be assumptions.
Try run this to debug (this is not the answer yet)
SELECT s.username, p.id, COUNT( v.id ) AS cnt
FROM `instagram_item_viewer` v
INNER JOIN `instagram_shop_picture` p ON v.item_id = p.id
INNER JOIN `instagram_shop` s ON p.shop_id = s.id
AND s.expirydate IS NULL
AND s.isLocked =0
AND v.created >= '2014-08-01'
GROUP BY (
s.username, p.id
)
ORDER BY cnt DESC
The problem here is the store and item viewer is too far apart (i.e. bridged via shop_picture). Thus shop_picture needs to be in the SELECT statement.
Your original query only gets the first shop_picture count for that store that is why it is less than expected
Ultimately if you still want to achieve your goal, you can expand my SQL above to
SELECT x.username, SUM(x.cnt) -- or COUNT(x.cnt) depending on what you want
FROM
(
SELECT s.username, p.id, COUNT( v.id ) AS cnt
FROM `instagram_item_viewer` v
INNER JOIN `instagram_shop_picture` p ON v.item_id = p.id
INNER JOIN `instagram_shop` s ON p.shop_id = s.id
AND s.expirydate IS NULL
AND s.isLocked =0
AND v.created >= '2014-08-01'
GROUP BY (
s.username, p.id
)
ORDER BY cnt DESC
) x
GROUP BY x.username

MySQL SELECT subquery

I have a calendar and user_result table and I need to join these two queries.
calendar query
SELECT `week`, `date`, `time`, COUNT(*) as count
FROM `calendar`
WHERE `week` = 1
GROUP BY `date`
ORDER BY `date` DESC
and the result is
{"week":"1","date":"2014-08-21","time":"15:30:00","count":"4"}, {"week":"1","date":"2014-08-20","time":"17:30:00","count":"12"}
user_result query
SELECT `date`, SUM(`point`) as score
FROM `user_result`
WHERE `user_id` = 1
AND `date` = '2014-08-20'
and the result is just score 3
My goal is to always show calendar even if the user isn't present in the user_result table, but if he is, SUM his points for that day where calendar.date = user_result.date. Result should be:
{"week":"1","date":"2014-08-21","time":"15:30:00","count":"4","score":"3"}, {"week":"1","date":"2014-08-20","time":"17:30:00","count":"12","score":"0"}
I have tried this query below, but the result is just one row and unexpected count
SELECT c.`week`, c.`date`, c.`time`, COUNT(*) as count, SUM(p.`point`) as score
FROM `calendar` c
INNER JOIN `user_result` p ON c.`date` = p.`date`
WHERE c.`week` = 1
AND p.`user_id` = 1
GROUP BY c.`date`
ORDER BY c.`date` DESC
{"week":"1","date":"2014-08-20","time":"17:30:00","count":"4","score":"9"}
SQL Fiddle
ow sorry, i was edited, and i was try at your sqlfiddle, if you want to show all date from calendar you can use LEFT JOIN, but if you want to show just the same date between calendar and result you can use INNER JOIN, note: in this case INNER JOIN just show 1 result, and LEFT JOIN show 2 results
SELECT c.`week`, p.user_id, c.`date`, c.`time`, COUNT(*) as count, p.score
FROM `calendar` c
LEFT JOIN
(
SELECT `date`, SUM(`point`) score, user_id
FROM `result`
group by `date`
) p ON c.`date` = p.`date`
WHERE c.`week` = 1
GROUP BY c.`date`
ORDER BY c.`date` DESC
I put a pre-aggreate query / group by date as a select for the one person you were interested in... then did a left-join to it. Also, your column names of week, date and time (IMO) are poor choice column names as they can appear to be too close to reserved keywords in MySQL. They are not, but could be confusing..
SELECT
c.week,
c.date,
c.time,
coalesce( OnePerson.PointEntries, 0 ) as count,
coalesce( OnePerson.totPoints, 0 ) as score
FROM
calendar c
LEFT JOIN ( select
r.week,
r.date,
COUNT(*) as PointEntries,
SUM( r.point ) as totPoints
from
result r
where
r.week = 1
AND r.user_id = 1
group by
r.week,
r.date ) OnePerson
ON c.week = OnePerson.week
AND c.date = OnePerson.date
WHERE
c.week = 1
GROUP BY
c.date
ORDER BY
c.date DESC
Posted code to SQLFiddle

GROUP_CONCAT multiple values from table

I just added a table which holds a url and description for each item in place. The below query works ok ... but it's only returning the url and I need the desc also. I m not sure how to make this work.
$query_str = "SELECT a.userid, a.cat, a.id, a.name, a.image,
a.desc, a.country, b.user_id, c.username,
c.fbook, d.cat_id,
(
SELECT GROUP_CONCAT(url)
FROM one_add o
WHERE a.id=o.one_id
) AS url,
(
SELECT COUNT(id)
FROM one_msg m
WHERE m.guest_id = ? AND
m.one_id=a.id
) AS count
FROM place a
LEFT OUTER JOIN wait b
ON a.id=b.post_id AND
b.user_id= ?
JOIN users c
ON a.userid=c.id
LEFT JOIN (
SELECT userid, user_id,
GROUP_CONCAT( cat_id ) AS cat_id
FROM user_cat
WHERE userid='$user_id'
GROUP BY user_id
) AS d ON d.userid='$user_id' AND
d.user_id=a.userid
WHERE a.cat != ? ORDER BY a.date desc";
This is what I want to accomplish: desc
You can achibe this by using CONCAT inside GROUP_CONCAT function as:
(SELECT GROUP_CONCAT(CONCAT('',desc,''))
FROM one_add o
WHERE a.id=o.one_id) AS url
full query:
(SELECT COUNT(id) FROM one_msg m WHERE m.guest_id = ? AND m.one_id=a.id ) AS count
FROM place a
LEFT OUTER JOIN wait b ON a.id=b.post_id AND b.user_id= ?
JOIN users c ON a.userid=c.id
LEFT JOIN ( SELECT userid, user_id, GROUP_CONCAT( cat_id ) AS cat_id FROM user_cat WHERE userid='$user_id' GROUP BY user_id ) AS d ON d.userid='$user_id' AND d.user_id=a.userid
WHERE a.cat != ? ORDER BY a.date desc";