How do I select 5 rows, 1 for each site_id, this is throwing an error
SELECT DISTINCT site_id, *
FROM deal
WHERE site_id IN (2, 3, 4, 5, 6)
ORDER BY id
DESC LIMIT 5
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '* FROM deal WHERE site_id IN (2, 3, 4, 5, 6) ORDER BY id DESC LIMIT 5' at line 1"
Try using GROUP BY
SELECT *
FROM deal
WHERE site_id IN (2, 3, 4, 5, 6)
GROUP BY site_id
ORDER BY id DESC
LIMIT 5;
First get one particular deal ids (the maximum one) for each site. (The inner query.)
Then get the full row for each of those deal ids. (The outer query.)
SELECT * FROM deal
WHERE id in (
SELECT max(id) maxid
FROM deal
WHERE site_id IN (2, 3, 4, 5, 6)
GROUP_BY site_id
)
You can remove following line if you're really interested in getting one row for each site for all of the sites in the database.
WHERE site_id IN (2, 3, 4, 5, 6)
If your table allows duplicate site_ids, and you only need to show one per site_id, then assuming ID is unique
SELECT * FROM deal
WHERE id in (
SELECT max(id) maxid
FROM deal
WHERE site_id IN (2, 3, 4, 5, 6)
GROUP by site_id
)
You can't specify DISTINCT site_id and also include the * wildcard. If you want to specify the distinct site_id, then you need to remove the wildcard and specify the other fields you want to use.
Related
I need to retrieve the latest model in a relationship, from a collection of records that belong to a set of users. The best I've come up with is:
SELECT
`answers`.*,
answers.created_at,
`questions`.`survey_id` AS `laravel_through_key`
FROM
`answers`
INNER JOIN
`questions` ON `questions`.`id` = `answers`.`question_id`
WHERE
`questions`.`id` IN (4, 5, 6)
AND `user_id` IN (1 , 2, 3)
group by user_id, question_id
ORDER BY `created_at` DESC
The tables are:
questions
id, text
answers
id, user_id (belongs to a user), question_id (belongs to a question)
users
id, name
For a set of users with IDs 1, 2, 3 - I want to retrieve the set of answers to questions with IDs 4, 5, 6 - but I only want each user's most recent answer for each question. In other words, there should only be a single answer for each user/question combination. I thought using GROUP_BY would do the trick, but I don't get the most recent answer. I'm using Laravel, but the issue is more a SQL one rather than a Laravel specific problem.
In MySQL 8 or later you can use window functions to find greatest n per group:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id, question_id ORDER BY created_at DESC) AS rn
FROM answers
WHERE user_id IN (1 , 2, 3) AND question_id IN (4, 5, 6)
)
SELECT *
FROM cte
WHERE rn = 1
I apologize for the possible incorrectness in the presentation, I use a translator. Let's say there is a users table in which there is an id field. And there is a list that lists the id numbers and some of them are repeated. My query
select id, count(*)
from users
where id in (3, 10, 10, 10)
group by id;
returns the following 3 - 1, 10 - 1. And I would like to get 3 - 1, 10 - 3, and so on. Is it possible to get it somehow?
UPD.
The data in the list (3, 10, 10, 10) is just an example, the exact number of digits is not known because they are returned from another question.
You would need to use a join. You can put the values in a derived table for this:
select id, count(*)
from users u join
(select 3 as id union all
select 10 as id union all
select 10 as id union all
select 10 as id union all
) i
using(id)
group by id;
Would like to return one row per group, where the one is selected by multiple sort columns. Treading lightly here in the land of greatest-n-per-group to avoid a duplicate question.
SCHEMA:
CREATE TABLE logs (
id INT NOT NULL,
ip_address INT NOT NULL,
status INT NOT NULL,
PRIMARY KEY id
);
DATA:
INSERT INTO logs (id, ip_address, status)
VALUES ('1', 19216800, 1),
('2', 19216801, 2),
('3', 19216800, 2),
('4', 19216803, 0),
('5', 19216804, 0),
('6', 19216803, 0),
('7', 19216804, 1);
CURRENT QUERY:
SELECT *
FROM logs
ORDER BY ip_address, status=1 DESC, id DESC
Note: sorting by status=1 effectively turns the status column into a boolean. The tie breaker after status=1 is id. This query currently returns the correct row for each ip_address first and then a bunch of other rows I don't want for that ip_address.
CURRENT OUTPUT:
1, 19216800, 1
3, 19216800, 2
2, 19216801, 2
6, 19216803, 0
4, 19216803, 0
7, 19216804, 1
5, 19216804, 0
WANTED OUTPUT:
1, 19216800, 1
2, 19216801, 2
6, 19216803, 0
7, 19216804, 1
Today my workaround is to filter in PHP with if ($lastIP == $row['ip_address']) continue;. But I would like to move this logic to MySQL.
Try this -
SELECT MIN(id), ip_address, status
FROM logs
GROUP BY ip_address, status
Since there are already hundreds of solutions for greatest-n-per-group problems in MySQL, I'm going to start answering these questions with CTE syntax with window functions, since that is now available in MySQL 8.0.3.
WITH sorted AS (
SELECT id, ip_address, status,
ROW_NUMBER() OVER (PARTITION BY ip_address ORDER BY status) AS rn
FROM logs
)
SELECT * FROM sorted WHERE rn = 1;
Here is different way to think about the problem. You want to find the "best" row for each id_address. Or in other words, you want to select rows where no better row exists.
This solution works for MySQL versions before 8.0. In other words, it works with the version you already have installed with RHEL 7. You can extend this technique easily for an arbitrary number of sort columns.
SELECT a.*
FROM (SELECT * FROM logs) a
LEFT JOIN (SELECT * FROM logs) b
ON (b.ip_address = a.ip_address AND (b.stat=1) > (a.stat=1))
OR (b.ip_address = a.ip_address AND (b.stat=1) = (a.stat=1) AND b.id > a.id)
WHERE b.id IS NULL
ORDER BY a.ip_address
If you have more columns to sort by then keeping adding OR clauses to handle tie breaks and select the "best" row for each ip_address. Regardless how complicated your subquery is or how many "SORT BY~ conditions you have, you will only need one LEFT JOIN with this technique.
Try this:
SELECT
l.`ip_address` , l.`status`
FROM
`logs` l
GROUP BY l.`ip_address`
ORDER BY l.`status` = 1 DESC
My goal is to select a random business and then with that business' id get all of their advertisements. I am getting unexpected results from my query. The number of advertisement rows returned is always what I assume is the value of "SELECT id FROM Business ORDER BY RAND() LIMIT 1". I have 3 businesses and only 1 business that has advertisement rows (5 of them) yet it always displays between 1-3 of the 5 advertisements for the same business.
SELECT * FROM Advertisement WHERE business_id=(SELECT id FROM Business ORDER BY RAND() LIMIT 1) ORDER BY priority
Business TABLE:
Advertisement TABLE:
Data for Advertisement and Business tables:
INSERT INTO `Advertisement` (`id`, `business_id`, `image_url`, `link_url`, `priority`) VALUES
(1, 1, 'http://i64.tinypic.com/2w4ehqw.png', 'https://www.dennys.com/food/burgers-sandwiches/spicy-sriracha-burger/', 1),
(2, 1, 'http://i65.tinypic.com/zuk1w1.png', 'https://www.dennys.com/food/burgers-sandwiches/prime-rib-philly-melt/', 2),
(3, 1, 'http://i64.tinypic.com/8yul3t.png', 'https://www.dennys.com/food/burgers-sandwiches/cali-club-sandwich/', 3),
(4, 1, 'http://i64.tinypic.com/o8fj9e.png', 'https://www.dennys.com/food/burgers-sandwiches/bacon-slamburger/', 4),
(5, 1, 'http://i68.tinypic.com/mwyuiv.png', 'https://www.dennys.com/food/burgers-sandwiches/the-superbird/', 5);
INSERT INTO `Business` (`id`, `name`) VALUES
(1, 'Test Dennys'),
(2, 'Test Business 2'),
(3, 'Test Business 3');
You're assuming your query does something it doesn't do.
(SELECT id FROM Business ORDER BY RAND() LIMIT 1) isn't materialized at the beginning of the query. It's evaluated for each row... so for each row, we're testing whether that business_id matches the result of a newly-executed instance of the subquery. More thorough test data (more than one business included) should reveal this.
You need to materialize the result into a derived table, then join to it.
SELECT a.*
FROM Advertisement a
JOIN (
SELECT (SELECT id
FROM Business
ORDER BY RAND()
LIMIT 1) AS business_id
) b ON b.business_id = a.business_id;
The ( SELECT ... ) x construct creates a temporary table that exists only for the duration of the query and uses the alias x. Such tables can be joined just like real tables.
MySQL calls this a Subquery in the FROM Clause.
Try following query
SELECT * FROM Advertisement WHERE business_id = (select floor(1 + rand()* (select count(*) from Business)));
To retrieve rows in random order use SELECT * Instead Of Id and then query for its id.
SELECT * FROM Advertisement WHERE business_id=(SELECT ID FROM (SELECT * FROM Business ORDER BY RAND() LIMIT 1) as table1)
In this case with your example data, only when rand returns 1 you get results.
I'm looking for a query to know how many times the value of a field is repeated in a select?
I prove with:
$queryIds ="select id,idpol,count(idpol) as qid from table WHERE id IN($idafectades) group by idpol";
But I'm looking for something like:
id, idpol, count_idpol (number of times that each idpol's value appears in the group of rows)
1, 1000, 3
2, 1000, 3
3, 1002, 1
4, 1003, 2
5, 1003, 2
6, 1000, 3
Thanks
This is one way without using a windowed set. Generate a set of data containing the counts grouped by the IDPOL, then join it back to the base set of each ID and IDpol.
SELECT foo.id, foo.idpol, B.cnt
FROM foo
INNER JOIN (SELECT count(*) cnt, IdPOL FROM Foo GROUP BY IDPOL) B
on Foo.IDPOL = B.IDPOL
SQL Fiddle