Grouping and chunking by value in MySQL - php

I have a simple raw SQL query which is returning data like this:
SELECT
a.id, count(ah.id) as count
FROM
`table_name_one` as a
JOIN
`table_name_two` as ah
ON
a.id=ah.answer_id
WHERE
a.user_id = 7178
AND a.created_at BETWEEN '2017-05-01' AND '2017-05-28'
GROUP BY a.id
I want to group this result by two groups.
Group One will contain data when revisionCount == 1 and Group Two will contain rest of the data. I can do this by using a loop but if I can do this by SQL query it will be good for me.
Can anybody help me out from here?

If you want to construct a list of the two sets of values, you can do so in one query:
SELECT GROUP_CONCAT(CASE WHEN cnt = 1 THEN id END) as id_1s,
GROUP_CONCAT(CASE WHEN cnt > 1 THEN id END) as id_2plus
FROM (SELECT a.id, count(ah.id) as cnt
FROM `table_name_one` a JOIN
`table_name_two` as ah
ON a.id = ah.answer_id
WHERE a.user_id = 7178 AND
a.created_at BETWEEN '2017-05-01' AND '2017-05-28'
GROUP BY a.id
) a;
This puts the values into comma-delimited lists. MySQL by default limits the length to 1,024 characters. If the lists are longer, then you can run the query twice to generate each . . . or just order by cnt and check the value when you are reading the return values.

You can use having clause to achieve this. I would recommend you to read online for more info on having.
Below query will give you all records with count exactly as 1:
SELECT
a.id, count(ah.id) as count
FROM
`table_name_one` as a
JOIN
`table_name_two` as ah
ON
a.id=ah.answer_id
WHERE
a.user_id = 7178
AND a.created_at BETWEEN '2017-05-01' AND '2017-05-28'
GROUP BY a.id
HAVING count(ah.id) = 1
You can tweak the having condition in the above query to get the second set.

I assume you need 2 result sets as arrays in php, for which you need to write 2 queries separated by semicolon.
Query 1 where revision count =1;
Query 2 where revision count >1;
Hope this helps

Related

How we can get the data from the table by limiting the number of identical columns MySql

Yesterday I tried to retrieve data from my db table using 'user_id' as a criterion to limit the amount of data per user.
I tried to get data from table https://prnt.sc/p53zhp in format like this https://prnt.sc/p541wk and limit the number of output records for user_id where limit will be 2 (count(user_id) <= 2), but i don't understand how to do that. What kind of sql request can i use to get this data?
Assuming that your RDBMS, here is a solution yo select only the top 2 records per user. You can use ROW_NUMBER() in a subquery to rank records by id within groups of records having the same user_id, and the filter out unerelevant records in the outer query, like:
SELECT *
FROM (
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY id)
FROM mytable
) x WHERE rn <= 2
On earlier versions of MySQL, you could use self-LEFT JOIN the table and use GROUP BY and HAVING COUNT(...) < 2 to limit the results to first two records per group:
SELECT
t.id,
t.user_id,
t.vip,
t.title,
t.description,
t.data
FROM mytable t
LEFT JOIN mytable t1 ON t1.user_id = t.user_id AND t1.id > t.id
GROUP BY
t.id,
t.user_id,
t.vip,
t.title,
t.description,
t.data
HAVING COUNT(t1.id) < 2
I don't understand if your problem is a Transact-SQL or your code.
In SQL you can limit record with "LIMIT": https://www.w3schools.com/sql/sql_top.asp
In code, you can use a condition IF.

MySql - how to calculate count(*) for query with group by?

I have the query :
select sellers.* from sellers
left join locations
on locations.seller_id = sellers.id
group by sellers.id
limit 0, 10;
Let assume that first query gives me 10 results with a group by, 15 results without a group by.
Now I want to calculate "all results count" for the pagination.
select count(*) from ...
I tried this :
select count(*) from sellers
left join locations
on locations.seller_id = sellers.id
//group by sellers.id;
1.WITH "group by sellers.id" I get 10 results with value 1 (it should be one result with value 10)
2.WITHOUT "group by sellers.id" I get one result with value 15 (it should be one result with value 10)
any ideas?
This should work for you:
select count(*) from (select sellers.id from sellers left join locations on locations.seller_id = sellers.id) as a;
You can put your SELECT query inside a SELECT COUNT(*) query, this way:
SELECT count(*) FROM (select sellers.* from sellers
left join locations
on locations.seller_id = sellers.id
group by sellers.id) sellers;
I don't approve of your query, but you simply want the count of sellers. The simplest method is:
select count(*)
from sellers;

using group by and order by in mysql query correctly

I'm trying to get the latest certificate a user has from the database. I only want to see the latest one and not all the others so I'm using group by and then ordering by the unique id from the main table.
Without group by this works perfectly. I see the last certificate uploaded and then all the others below.
As soon as I add the group by I see the first certificate ever uploaded which is pointless as it could be from years ago.
My query is quite large as I'm drawing in a lot of other information from other tables.
Here is my query.
SELECT
usercert.*,
cert.*,
certCat.certCatName,
certTask.certTaskName ,
certStatus.certStatusName
FROM
`usercert`
INNER JOIN
cert
ON
cert.idcert = usercert.idcert
INNER JOIN
certCat
ON
certCat.idcertCat = cert.idcertCat
INNER JOIN
certTask
ON
certTask.idcertTask = usercert.idcertTask
INNER JOIN
certStatus
ON
certStatus.idcertStatus = usercert.idcertStatus
WHERE
usercert.iduser=%s
GROUP BY
usercert.idcert
ORDER BY
usercert.usercertEnd DESC
SELECT
usercert.*,
cert.*,
certCat.certCatName,
certTask.certTaskName ,
certStatus.certStatusName
FROM
`usercert`
INNER JOIN
cert
ON
cert.idcert = usercert.idcert
INNER JOIN
certCat
ON
certCat.idcertCat = cert.idcertCat
INNER JOIN
certTask
ON
certTask.idcertTask = usercert.idcertTask
INNER JOIN
certStatus
ON
certStatus.idcertStatus = usercert.idcertSttus
WHERE
usercert.iduser=%s
ORDER BY
usercert.usercertEnd
DESC limit 0,1
in this query it will take all the record in descending order it means the last inserted row will come first and the limit 0,1 means it will start from 0 and fetch 1 record that's it ...
You can either use MAX():
SELECT ... FROM ... WHERE ... ORDER BY MAX(usercert.usercertEnd)
Or LIMIT:
SELECT ... FROM ... WHERE ... ORDER BY usercert.usercertEnd DESC LIMIT 1

How can i optimize MySQL query, event if it have index

Here is my query which is taking 17.9397 sec time to get response:
SELECT allbar.iBarID AS iBarID,
allbar.vName AS vName,
allbar.tAddress AS tAddress,
allbar.tDescription AS tDescription,
(SELECT COUNT(*)
FROM tbl_post p
WHERE p.vBarIDs = allbar.iBarID) AS `total_post`,
allbar.bar_usbg AS bar_usbg,
allbar.bar_enhance AS bar_enhance,
(SELECT count(*)
FROM tbl_user
WHERE FIND_IN_SET(allbar.iBarID,vBarIDs)
AND (eType = 'Bartender'
OR eType = 'Bar Manager'
OR eType = 'Bar Owner')) AS countAss,
allbar.eStatus AS eStatus
FROM
(SELECT DISTINCT b.iBarID AS iBarID,
b.vName AS vName,
b.tAddress AS tAddress,
(CASE LENGTH(b.tDescription) WHEN 0 THEN '' WHEN LENGTH(b.tDescription) > 0
AND LENGTH(b.tDescription) < 50 THEN CONCAT(LEFT(b.tDescription, 50),'...') ELSE b.tDescription END) AS tDescription,
b.usbg AS bar_usbg,
b.enhance AS bar_enhance,
b.eStatus AS eStatus
FROM tbl_bar b,
tbl_user u
WHERE b.iBarID <> '-10') AS allbar
I have tried EXPLAIN, here is the result of that:
Can anyone explain me this EXPLAIN result?
You should totaly rewrite that query, it's complete nonsense.
In this part
(SELECT DISTINCT b.<whatever>
FROM tbl_bar b,
tbl_user u
WHERE b.iBarID <> '-10') AS allbar
what you're basically doing is connecting every row from table tbl_bar with every row from tbl_user. Then filter tbl_bar, and when everything is selected (maybe MySQL has to write everything in a temporary table before doing this) return the result set without duplicates. You don't ever want to do that. Especially when you don't even select anything from tbl_user. When there's a connection, specify it. If there's none, don't join those tables or create a connection. I don't know if or how your tables are connected, but it should look something like this:
(SELECT DISTINCT b.<whatever>
FROM tbl_bar b
JOIN tbl_user u ON b.user_id = u.id /*or whatever the connection is*/
WHERE b.iBarID <> '-10') AS allbar
Then you have this ugly subquery.
(SELECT COUNT(*)
FROM tbl_post p
WHERE p.vBarIDs = allbar.iBarID) AS `total_post`,
allbar.bar_usbg AS bar_usbg,
allbar.bar_enhance AS bar_enhance,
which is by the way dependent (see your explain output). Which means, that this subquery is executed for every row of your outer query (yes, the one with the cross join as discussed above). Instead of this subquery, join the table in the outer query and work with GROUP BY.
So far the query should look something like this:
SELECT
b.iBarID AS iBarID,
b.vName AS vName,
b.tAddress AS tAddress,
b.tDescription AS tDescription,
COUNT(*) AS `total_post`,
allbar.bar_usbg AS bar_usbg,
allbar.bar_enhance AS bar_enhance
FROM
tbl_bar b
JOIN tbl_user u ON b.user_id = u.id
JOIN tbl_post p ON p.vBarIDs = b.iBarID
WHERE b.iBarID <> '-10'
GROUP BY b.iBarID
(In fact, this is not really right. Rule is, every column in the SELECT clause should either be in the GROUP BY clause as well or have an aggregate function (like count() or max() applied to it. Otherwise a random row of each group is displayed. But this is just an example. You will have to work out the details.)
Now comes the worst part.
(SELECT count(*)
FROM tbl_user
WHERE FIND_IN_SET(allbar.iBarID,vBarIDs)
AND (eType = 'Bartender'
OR eType = 'Bar Manager'
OR eType = 'Bar Owner')) AS countAss,
allbar.eStatus AS eStatus
The use of FIND_IN_SET() suggests, that you're storing multiple values in one column. Again, you never ever want to do that. Please read this answer to Is storing a delimited list in a database column really that bad? and then redesign your database. I won't help you with this one, as this clearly is stuff for a separate question.
All this didn't really explain the EXPLAIN result. For this question, I would have to write a whole tutorial, which I won't do, since everything is in the manual, as always.

Returning duplicate results SQL?

I have the following query:
SELECT DISTINCT
SQL_CALC_FOUND_ROWS
unr.RequestID,
unr.UnRead,
unr.FilterID,
r.GroupID,
r.Year,
rv.Bounty
FROM (users_notify_requests as unr, requests_votes as rv)
JOIN requests AS r ON r.ID = unr.RequestID
WHERE unr.UserID = 1 ORDER BY unr.RequestID DESC LIMIT 50
This should return only 2 rows, as there are only two requests where unr.UserID = 1, however it returns 10. 5 versions of the first, and 5 versions of the second, completely identical (respectively). Any idea as to why this might be happening?
EDIT: MySQL version 5.5.29, as requested.
EDIT 2: The print_r() dump: http://pastebin.com/BXujnEpx. The result has incorrect bounty for the given IDs, so something is pretty off with the query.
You're not joining on rv with any criteria, so every row in that table will be returned: It could be that this is the cause of your extra rows.
You probably need a line such as the following (guessing at column names as I don't have your schema):
INNER JOIN request_votes rv ON rv.requestId = r.id
you are joining two tables based on cartesian product or cross join .
FROM (users_notify_requests as unr, requests_votes as rv)
And then you are using a inner join.
JOIN requests AS r ON r.ID = unr.RequestID
To solve the problem you should use inner join in both cases.
Cartesian join is when you join every row of one table to every row of another table. if 1st table contain x rows and y rows in 2nd one the result set will be x*y rows.
SELECT DISTINCT
SQL_CALC_FOUND_ROWS
unr.RequestID,
unr.UnRead,
unr.FilterID,
r.GroupID,
r.Year,
rv.Bounty
FROM users_notify_requests as unr
JOIN requests_votes as rv ON rv.RequestID = unr.RequestID
JOIN requests AS r ON r.ID = unr.RequestID
WHERE unr.UserID = 1 ORDER BY unr.RequestID DESC LIMIT 50

Categories