Problem of greatest-n-per-group and my MySQL version cannot use the LIMIT & IN (error 1235), so I need to use this kind of query (see answer here: answer )
SELECT
t1.idMemberCard,
DATE(MIN(transactions.dateTransaction)) AS first_transaction,
DATE(MAX(transactions.dateTransaction)) AS last_transaction,
t2.*
FROM membersCard AS t1
INNER JOIN transactions ON transactions.idMemberCard = t1.idMemberCard
INNER JOIN
(
SELECT
membersCard.idMemberCard,
membersCard.cardNumber,
membersCard.firstNameMemberCard,
membersCard.lastNameMemberCard,
transactions.dateTransaction
FROM membersCard
INNER JOIN transactions ON transactions.idMemberCard = membersCard.idMemberCard
WHERE membersCard.sexMemberCard = 'M'
AND membersCard.cardNumber = '1100101308655'
AND
DATE(transactions.dateTransaction) BETWEEN ('2013-12-28') AND ('2014-08-13')
LIMIT 100
) AS t2
ON t1.idMemberCard = t2.idMemberCard
Subquery (t2 table) executed for exact match (card Number) returns exactly 5 rows (in this example): all perfect.
My issue/request:
Joining the two tables I would to obtain 5 rows and not only one, with the different 5 rows with the dates.
Your join is working fine. The problem is your select statement:
SELECT t1.idMemberCard,
DATE(MIN(transactions.dateTransaction)) AS first_transaction,
DATE(MAX(transactions.dateTransaction)) AS last_transaction,
t2.*
The use of MIN() and MAX() turn this into an aggregation query that returns only one row. Try this:
SELECT t1.idMemberCard,
DATE(transactions.dateTransaction) AS first_transaction,
DATE(transactions.dateTransaction) AS last_transaction,
t2.*
Or add a group by clause if you want that.
Related
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.
I have a mysql query like
SELECT `tbl_ticket`.`id`, `tbl_ticket`.`hd_user_username`,
`tbl_ticket`.`hd_user_email`, `tbl_ticket`.`ticket_title`,
`tbl_complain_type`.`complains` FROM `tbl_ticket` LEFT JOIN
`tbl_ticket_complain` ON tbl_ticket_complain.ticket_id=tbl_ticket.id
LEFT JOIN `tbl_complain_type` ON tbl_complain_type.id=tbl_ticket_complain.complain_id
LEFT JOIN `tbl_assignment` ON tbl_assignment.ticket_id=tbl_ticket.id
WHERE ((((`hd_user_username` LIKE '%searchterm%')
AND (`tbl_assignment`.`id` IN ($array)))
OR (`hd_user_email`='searchterm'))
OR (`ticket_title`='searchterm')) OR (`tbl_complain_type`.`complains`='searchterm')
$array contains around 7000 values like `$array=array(1,2,3,..)`
This query takes around 8 seconds to execute. Is there any alternative solution for this query ?
The value of $array is got from another query
select max(id) from tbl_assignment group by ticket_id
The slowness of query is due to multiple joins between tables
If the values in the array use in you IN clause come from a select you could use the fact that
An IN clause is equivalent to an inner join so you could use a inner join between your_table_with_id and the table.column you need for match eg:
SELECT `
tbl_ticket`.`id`
, `tbl_ticket`.`hd_user_username`
, `tbl_ticket`.`hd_user_email`
, `tbl_ticket`.`ticket_title`
, `tbl_complain_type`.`complains`
FROM `tbl_ticket`
LEFT JOIN `tbl_ticket_complain` ON tbl_ticket_complain.ticket_id=tbl_ticket.id
LEFT JOIN `tbl_complain_type` ON tbl_complain_type.id=tbl_ticket_complain.complain_id
LEFT JOIN `tbl_assignment` ON tbl_assignment.ticket_id=tbl_ticket.id
INNER JOIN your_table_with_id ON `tbl_assignment`.`id` = JOIN your_table_with_id.id
WHERE ((((`hd_user_username` LIKE '%searchterm%')
OR (`hd_user_email`='searchterm'))
OR (`ticket_title`='searchterm')) OR (`tbl_complain_type`.`complains`='searchterm')
Remeber also that the content of values use IN clause is limited and fail when the limit is exceeded
and in your case
SELECT `
tbl_ticket`.`id`
, `tbl_ticket`.`hd_user_username`
, `tbl_ticket`.`hd_user_email`
, `tbl_ticket`.`ticket_title`
, `tbl_complain_type`.`complains`
FROM `tbl_ticket`
LEFT JOIN `tbl_ticket_complain` ON tbl_ticket_complain.ticket_id=tbl_ticket.id
LEFT JOIN `tbl_complain_type` ON tbl_complain_type.id=tbl_ticket_complain.complain_id
LEFT JOIN `tbl_assignment` ON tbl_assignment.ticket_id=tbl_ticket.id
INNER JOIN (
select max(id) as id
from tbl_assignment
group by ticket_id
) t ON `tbl_assignment`.`id` = t.id
WHERE ((((`hd_user_username` LIKE '%searchterm%')
OR (`hd_user_email`='searchterm'))
OR (`ticket_title`='searchterm')) OR (`tbl_complain_type`.`complains`='searchterm'))
This is basically your query:
SELECT . . .
FROM tbl_ticket t LEFT JOIN
tbl_ticket_complain tc
ON tc.ticket_id = t.id LEFT JOIN
tbl_complain_type tct
ON tct.id = tc.complain_id LEFT JOIN
tbl_assignment a
ON a.ticket_id = t.id
WHERE (((hd_user_username LIKE '%searchterm%' AND
a.id IN ($array)
) OR
`hd_user_email`='searchterm'
) OR
ticket_title = 'searchterm'
) OR
tct.complain` = 'searchterm';
The issue with performance has nothing to do with IN. In fact, MySQL optimizes IN, as explained in the documentation:
If all values are constants, they are evaluated according to the type
of expr and sorted. The search for the item then is done using a
binary search. This means IN is very quick if the IN value list
consists entirely of constants.
You are not going to get faster than an IN list with constants.
The problem with your query is the string of ORs. These make is almost impossible for the optimizer to use indexes -- so the full result set has to be created and then filtered down.
It is hard for me to see how to improve this in your query. Sometimes, splitting a query into simpler chunks and connecting them using union or union all does the trick. Your conditions are a bit hard to follow, making that approach difficult for an outsider.
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
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
I have the following query
$query = $this->db->query(
'SELECT ii.json
FROM inventory ii
INNER JOIN
(
SELECT json, MAX(id) as MAX_ID
FROM inventory
GROUP BY business_unit
) group_json ON ii.id = group_json.MAX_ID
INNER JOIN business_units
ON ii.business_unit = business_units.id'
);
return $query->result_array();
We've had to migrate to a MSSQL test server and unfortunately the syntax is incorrect. To avoid this in the future I want to convert this to the CodeIgniter typed query.
Can anyone show me what this query would look like in CodeIgniter, I've tried a few things but I'm not getting anywhere close.
SELECT ii.json
FROM inventory ii
INNER JOIN
(
SELECT json, MAX(id) as MAX_ID
FROM inventory
GROUP BY business_unit, json --<-- "json" needs to be in GROUP BY
) group_json -- Since it is in SELECT but not
ON ii.id = group_json.MAX_ID -- contained in any aggregate function
INNER JOIN business_units
ON ii.business_unit = business_units.id
if you use any aggregate function(MAX, MIN, AVG, SUM, COUNT) in your SELECT any other columns that are not contained in any aggregate function in that select, must go in GROUP BY clause of your query.