MySQL: Select records where COUNT - mysql

I'm trying to select 1000 customers that have placed exactly 1 order. Everything is in the Orders table.
select * from Order having count(CustomerID) = 1 limit 1000
So basically all records that have only one occurence of the CustomerID in the entire table. This returns an empty result set and there are 100,000s in the table.

You need to GROUP in order to (meaningfully) use the HAVING clause. The following works as expected:
SELECT
*
FROM
`Order`
GROUP BY
CustomerID
HAVING
COUNT(CustomerID) = 1
LIMIT 1000
UPDATE
Adding WHERE (see comments):
SELECT
*
FROM
`Order`
WHERE
Language = 'EN'
GROUP BY
CustomerID
HAVING
COUNT(CustomerID) = 1
LIMIT 1000

SELECT * FROM `Order`
GROUP BY CustomerID
HAVING COUNT(CustomerID) = 1
LIMIT 1000

try
select count(CustomerID) as counter ,o.* from Order o
group by CustomerID having counter = 1 limit 1000

Add group by to query for correct output, e.g:
select *
from Order
group by CustomerID
having count(CustomerID) = 1
limit 1000

SELECT CustomerID FROM Order GROUP BY CustomerID HAVING COUNT(*) = 1 LIMIT 1000

Related

Get random records in MySql

I have a sql table, Employee which has 4 columns.
Id
Name
Address
Status – Fixed Type (Enum). Having 3 fixed values – OPEN, CLOSED and PENDING.
Let’s assume Total Records in the table are 200.
Now, I want to form a query which would return -
50 random records which have status “OPEN”.
25 random records which have status “CLOSED”.
45 random records which have status “PENDING”.
I have tried with the one fragmented query and it worked fine but looking for single query solution for whole scenario.
Select * from Employee a where a.Status = 'OPEN' ORDER BY RAND() LIMIT 50
Select * from Employee a where a.Status = 'CLOSED' ORDER BY RAND() LIMIT 25
Select * from Employee a where a.Status = 'PENDING' ORDER BY RAND() LIMIT 45
Any help would be highly appreciated.
Do you need it as a single query (mildly painful) or as 3 separate queries?
As 3 separate queries, the simplest option would be something like
Select * from Employee where Status = 'OPEN' ORDER BY RAND() LIMIT 50;
Select * from Employee where Status = 'CLOSED' ORDER BY RAND() LIMIT 25;
Select * from Employee where Status = 'PENDING' ORDER BY RAND() LIMIT 45;
Use Union to combine multiple queries and remember to assign alias for each sub/parent query.
select * from ((Select * from Employee a where a.Status = 'OPEN' ORDER BY RAND() LIMIT 50)
union
(Select * from Employee b where b.Status = 'CLOSED' ORDER BY RAND() LIMIT 25)
union
(Select * from Employee c where c.Status = 'PENDING' ORDER BY RAND() LIMIT 45)) d;
I hope this will be useful for someone.

Select sum of top three scores for each user

I am having trouble writing a query for the following problem. I have tried some existing queries but cannot get the results I need.
I have a results table like this:
userid score timestamp
1 50 5000
1 100 5000
1 400 5000
1 500 5000
2 100 5000
3 1000 4000
The expected output of the query is like this:
userid score
3 1000
1 1000
2 100
I want to select a top list where I have n best scores summed for each user and if there is a draw the user with the lowest timestamp is highest. I really tried to look at all old posts but could not find one that helped me.
Here is what I have tried:
SELECT sum(score) FROM (
SELECT score
FROM results
WHERE userid=1 ORDER BY score DESC LIMIT 3
) as subquery
This gives me the results for one user, but I would like to have one query that fetches all in order.
This is a pretty typical greatest-n-per-group problem. When I see those, I usually use a correlated subquery like this:
SELECT *
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score) <= 3;
This is not the whole solution, as it only gives you the top three scores for each user in its own row. To get the total, you can use SUM() wrapped around that subquery like this:
SELECT userId, SUM(score) AS totalScore
FROM(
SELECT userId, score
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score) <= 3) tmp
GROUP BY userId;
Here is an SQL Fiddle example.
EDIT
Regarding the ordering (which I forgot the first time through), you can just order by totalScore in descending order, and then by MIN(timestamp) in ascending order so that users with the lowest timestamp appears first in the list. Here is the updated query:
SELECT userId, SUM(score) AS totalScore
FROM(
SELECT userId, score, timeCol
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score) <= 3) tmp
GROUP BY userId
ORDER BY totalScore DESC, MIN(timeCol) ASC;
and here is an updated Fiddle link.
EDIT 2
As JPW pointed out in the comments, this query will not work if the user has the same score for multiple questions. To settle this, you can add an additional condition inside the subquery to order the users three rows by timestamp as well, like this:
SELECT userId, SUM(score) AS totalScore
FROM(
SELECT userId, score, timeCol
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score
AND mT.timeCol <= m.timeCol) <= 3) tmp
GROUP BY userId
ORDER BY totalScore DESC, MIN(timeCol) ASC;
I am still working on a solution to find out how to handle the scenario where the userid, score, and timestamp are all the same. In that case, you will have to find another tiebreaker. Perhaps you have a primary key column, and you can choose to take a higher/lower primary key?
Query for selecting top three scores from table.
SELECT score FROM result
GROUP BY id
ORDER BY score DESC
LIMIT 3;
Can you please try this?
SELECT score FROM result GROUP BY id ORDER BY score DESC, timestamp ASC LIMIT 3;
if 2 users have same score then it will set order depends on time.
You can use a subquery
SELECT r.userid,
( SELECT sum(r2.score)
FROM results r2
WHERE r2.userid = r.userid
ORDER BY score DESC
LIMIT 3
) as sub
FROM result r
GROUP BY r.userid
ORDER BY sub desc
You should do it like this
SELECT SUM(score) as total, min(timestamp) as first, userid FROM scores
GROUP BY userid
ORDER BY total DESC, first ASC
This is way more efficient than sub queries. If you want to extract more fields than userid, then you need to add them to the group by.
This will of cause not limit the number of scores pr user, which indeed seems to require a subquery to solve.

MySQL Query add limit in SubQuery

Here is my table test with values:
Price
----------
300
600
900
1000
1800
2000
I want to query such that when I search for 300 ,I should get 4 records 300,600,900,1000.
If I search for 900, I can get 600,900,1000,1800.
i.e. Two records <=900 and Two record >900
Here is the query I tried :
SELECT * FROM table h where CONDITIONS
and (price in (select price from table where price <=900) // I want to add LIMIT 2 in this subquery
or price in (select price from table where price >900)//LIMIT 2
)
order by FIELD(price ,900) DESC limit 5;
I searched a lot on stack overflow,but nothing worked. Please help .
You can try the following...
select * from ((select h.* from table_name h where amount <=300 order by amount desc limit 2)
union
(select h.* from table_name h where amount >300 order by amount limit 2))
derived_table order by FIELD(amount,300) desc;
MySQL doesn't support LIMIT in WHERE IN/EXSISTS/ANY/SOME subqueries, you can do this with UNION
(SELECT * /* this should be a columnlist */
FROM tablename
WHERE price < 900
ORDER BY price LIMIT 2)
UNION
(SELECT * /* this should be a columnlist */
FROM tablename
WHERE price >= 900
ORDER BY price LIMIT 2)
The parenthesis around each select are crucial.
See SQL Fiddle
Using union because this mysql version don't accept limit in subqueries
select * from table where price <=900 limit 2 union select * from table where price > 900 limit 2

MYSQL DISTINCT and ORDER BY together

I have a table like the following
item_id position_number position_date
1 9 2013-06-29 15:12:58
2 7 2013-07-25 15:12:58
18 5 2013-07-08 12:07:00
13 9 2013-07-08 12:07:00
I want to get the items group by position_number and order by position_date DESC, so the query will return the following:
item_id position_number position_date
13 9 2013-07-08 12:07:00
2 7 2013-07-25 15:12:58
18 5 2013-07-08 12:07:00
I've been implementing some of the solutions that use DISTINCT and GROUP BY, but not get the desired result.
Does anyone have an idea about how to solved it?
SELECT a.*
FROM tableName a
INNER JOIN
(
SELECT position_number, MAX(position_date) position_date
FROM tableName
GROUP BY position_number
) b ON a.position_number = b.position_number AND
a.position_date = b.position_date
ORDER BY a.position_number DESC
SQLFiddle Demo
Given your example data, this query will return the specified resultset:
SELECT t.item_id
, t.position_number
, t.position_date
FROM ( SELECT MAX(n.item_id) AS max_item_id
FROM mytable n
GROUP BY position_number
) m
JOIN mytable t
ON t.item_id = m.max_item_id
ORDER BY t.position_number DESC
NOTE This is choosing a single item_id for each position_number. This is assuming that a given item_id will appear only once, and have a single position_number. (If an item_id can be associated with multiple postion_number, the query can be tweaked. This is using the MAX() function to choose the item_id with the largest value. (The only example of a row being excluded is item_id=1.)
try this
select * from your_table group by position_number order by position_date DESC
EDIT:
SELECT `item_id`, max(`position_number`) position_number , max(`position_date`) position_date FROM TABLENAME
GROUP BY POSITION_NUMBER
ORDER BY POSITION_DATE DESC
DEMO
Here is SQLFiddle
SELECT * FROM TABLE_NAME
GROUP BY POSITION_NUMBER
ORDER BY POSITION_DATE DESC
Try this code:
SELECT * FROM TABLE_NAME GROUP BY POSITION_NUMBER ORDER BY POSITION_DATE DESC
Use GROUP_BY and either MIN or MAX to decide which date in the group you want to use for sorting.
SELECT * FROM my_table GROUP BY position_number ORDER BY MAX(position_date) DESC;

Retrieve the second latest record with same id

From the following four records, I want to select the OwnerId of second-latest record
ItemId OwnerId Date
11477 20981 2013-05-13
11477 1 2013-05-21
11477 21086 2013-05-22 #this is the one I'm talking about
11477 3868 2013-05-24
How to go about it?
This needs ItemID to be specified,
SELECT *
FROM TableName
WHERE ItemID = '11477'
ORDER BY DATE DESC
LIMIT 1,1
SQLFiddle Demo
However, if you don't want to specify the ItemID, and you want to get all second latest record for every ItemID, you can use a correlated subquery to generate a sequence number for every ItemID based on lastest DATE,
SELECT ItemId, OwnerID, Date
FROM
(
SELECT A.ItemId,
A.OwnerId,
A.Date,
(
SELECT COUNT(*)
FROM tableName c
WHERE c.ItemId = a.ItemId AND
c.Date >= a.Date) AS RowNumber
FROM TableName a
) x
WHERE RowNumber = 2
SQLFiddle Demo
select ownerid
from your_table
order by date desc
limit 1, 1
I think you can just to ORDER BY date descending, which will give you an order from newer to older, then LIMIT 1,1 to get only the second result, which should be the one you look for
SELECT *
FROM table
ORDER BY date DESC
LIMIT 1,1