I'm trying to select a group of list and get results in re-ordered number fashion.
e.g.
ID name
jack
jack
paul
bob
bob
paul
bob
Say I select name='bob' thus got its ID numbers 4,5,7
Now I want the results to be
in this order 1, 2, 3 ...
instead of 4, 5, 7 ...
because it's bob's first second and third, etc...
What's the easiest way to accomplish this?
Here mysql variables can do the work. Declare & initialize a variable #a = 0, assign an incremented value and select in query. The query should look like:
SET #a = 0;
SELECT #a := #a + 1 AS id, name FROM table_name WHERE name = 'bob';
SET #a = 0;
In the end, again set value of #a = 0.
If you are using Mysql 8 then you could use window function rank() and common table expression
with ordered_data as (
select *,
rank() over (partition by name order by id asc ) rnk
from your_table
order by rnk
)
select * from ordered_data where name = 'bob';
Or if you are using older release then you could use a correlated query to get the rank
select a.id,a.name,
(select count(*)
from your_table
where name= a.name
and id <= a.id) rnk
from your_table a
where a.name = 'bob'
order by a.id;
Demo
Related
I am not even sure if this has been answered because I don't even know how to coin the problem. But here is what am trying to do.
I am using COUNT() to create a tabular representation of a data from top to bottom for a 30 day period.
SELECT id FROM table WHERE col = '123' AND date >= DATE_SUB(CURRENT_DATE, INTERVAL DAYOFMONTH(CURRENT_DATE)-1 DAY) AND date <= LAST_DAY(CURRENT_DATE) GROUP BY id ORDER BY COUNT(id) DESC
And I get the result with the most at the top
id | col
==========
id3 | 123
id5 | 123
id2 | 123
id4 | 123
id8 | 123
id5 | 123
id1 | 123
id9 | 123
id7 | 123
This works fine for a tabular view and I can use ol to create a numbering system from 1 - 10. My issue is, I want to be able to tell the position of any given id. Eg. if I want to get the position of id9 in this count result i.e. 8, how do I do that?
If you are using MySQL v8.0 or higher you can use the RANK function:
SELECT COUNT(*), RANK() OVER (ORDER BY COUNT(id) DESC) AS r FROM table GROUP BY id ORDER BY COUNT(id) DESC;
For previous version of mysql, you need to create the variable your self:
SELECT COUNT(*), #rank := #rank + 1 AS r FROM table, (SELECT #rank := 0) temp ORDER BY COUNT(id) DESC;
Note SELECT #rank := 0 initiate the variable.
Updated:
To select a specific id and it's rank, you can use:
SELECT * FROM (
SELECT id, COUNT(*), RANK() OVER (ORDER BY COUNT(id) DESC) AS r FROM table GROUP BY id ORDER BY COUNT(id) DESC
) ranked WHERE id = ?;
I have a table. Table has structure of id, name, color, product_id.
And the table has multiple rows with the same product_id.
With SQL query from PHP file - I would like to choose only one, the oldest, row. (The first one that was added to the current table).
What query should I use or approach?
Thank you!
Just making up a bit of mockup data ... Note the notes I put in. And I trust it's a newer version of MySQL, as the older ones did not support ROW_NUMBER() OVER() .
Here goes:
WITH
-- input ... you *need* a timestamp to identify the oldest ---
indata(id, name, color, product_id,ts) AS (
SELECT 1,'Arthur','blue' ,42,TIMESTAMP'2021-01-31 17:45:00'
UNION ALL SELECT 1,'Arthur','blue' ,42,TIMESTAMP'2021-01-31 17:50:00'
UNION ALL SELECT 1,'Arthur','blue' ,42,TIMESTAMP'2021-01-31 17:55:00'
UNION ALL SELECT 1,'Arthur','blue' ,42,TIMESTAMP'2021-01-31 18:00:00'
UNION ALL SELECT 2,'Ford' ,'red' ,42,TIMESTAMP'2021-01-31 17:45:00'
UNION ALL SELECT 2,'Ford' ,'blue', 42,TIMESTAMP'2021-01-31 17:50:00'
UNION ALL SELECT 2,'Ford' ,'green',42,TIMESTAMP'2021-01-31 17:55:00'
UNION ALL SELECT 2,'Ford' ,'cyan' ,42,TIMESTAMP'2021-01-31 18:00:00'
)
,
-- select all, plus a rank, on which you will filter outside ..
with_rank AS (
SELECT
*
, ROW_NUMBER() OVER(PARTITION BY id ORDER BY ts) AS rnk
FROM indata
)
SELECT
id
, name
, color
, product_id
, ts
FROM with_rank
WHERE rnk = 1
id|name |color|product_id|ts
1|Arthur|blue |42 |2021-01-31 17:45:00
2|Ford |red |42 |2021-01-31 17:45:00
One method is a correlated subquery:
select t.*
from t
where t.id = (select min(t2.id)
from t t2
where t2.product_id = t.product_id
);
This assumes that id is incrementing with each insertion. If not, you have no way of knowing what the "oldest" row is. SQL tables represent unordered sets, so there is no "oldest" row unless a column contains that information.
SELECT * FROM TableName WHERE product_id = ProductID ORDER BY product_id LIMIT 1;
How can I find the most frequent value in a given column in an SQL table?
For example, for this table it should return two since it is the most frequent value:
one
two
two
three
SELECT
<column_name>,
COUNT(<column_name>) AS `value_occurrence`
FROM
<my_table>
GROUP BY
<column_name>
ORDER BY
`value_occurrence` DESC
LIMIT 1;
Replace <column_name> and <my_table>. Increase 1 if you want to see the N most common values of the column.
Try something like:
SELECT `column`
FROM `your_table`
GROUP BY `column`
ORDER BY COUNT(*) DESC
LIMIT 1;
Let us consider table name as tblperson and column name as city. I want to retrieve the most repeated city from the city column:
select city,count(*) as nor from tblperson
group by city
having count(*) =(select max(nor) from
(select city,count(*) as nor from tblperson group by city) tblperson)
Here nor is an alias name.
Below query seems to work good for me in SQL Server database:
select column, COUNT(column) AS MOST_FREQUENT
from TABLE_NAME
GROUP BY column
ORDER BY COUNT(column) DESC
Result:
column MOST_FREQUENT
item1 highest count
item2 second highest
item3 third higest
..
..
For use with SQL Server.
As there is no limit command support in that.
Yo can use the top 1 command to find the maximum occurring value in the particular column in this case (value)
SELECT top1
`value`,
COUNT(`value`) AS `value_occurrence`
FROM
`my_table`
GROUP BY
`value`
ORDER BY
`value_occurrence` DESC;
Assuming Table is 'SalesLT.Customer' and the Column you are trying to figure out is 'CompanyName' and AggCompanyName is an Alias.
Select CompanyName, Count(CompanyName) as AggCompanyName from SalesLT.Customer
group by CompanyName
Order By Count(CompanyName) Desc;
If you can't use LIMIT or LIMIT is not an option for your query tool. You can use "ROWNUM" instead, but you will need a sub query:
SELECT FIELD_1, ALIAS1
FROM(SELECT FIELD_1, COUNT(FIELD_1) ALIAS1
FROM TABLENAME
GROUP BY FIELD_1
ORDER BY COUNT(FIELD_1) DESC)
WHERE ROWNUM = 1
If you have an ID column and you want to find most repetitive category from another column for each ID then you can use below query,
Table:
Query:
SELECT ID, CATEGORY, COUNT(*) AS FREQ
FROM TABLE
GROUP BY 1,2
QUALIFY ROW_NUMBER() OVER(PARTITION BY ID ORDER BY FREQ DESC) = 1;
Result:
Return all most frequent rows in case of tie
Find the most frequent value in mysql,display all in case of a tie gives two possible approaches:
Scalar subquery:
SELECT
"country",
COUNT(country) AS "cnt"
FROM "Sales"
GROUP BY "country"
HAVING
COUNT("country") = (
SELECT COUNT("country") AS "cnt"
FROM "Sales"
GROUP BY "country"
ORDER BY "cnt" DESC,
LIMIT 1
)
ORDER BY "country" ASC
With the RANK window function, available since MySQL 8+:
SELECT "country", "cnt"
FROM (
SELECT
"country",
COUNT("country") AS "cnt",
RANK() OVER (ORDER BY COUNT(*) DESC) "rnk"
FROM "Sales"
GROUP BY "country"
) AS "sub"
WHERE "rnk" = 1
ORDER BY "country" ASC
This method might save a second recount compared to the first one.
RANK works by ranking all rows, such that if two rows are at the top, both get rank 1. So it basically directly solves this type of use case.
RANK is also available on SQLite and PostgreSQL, I think it might be SQL standard, not sure.
In the above queries I also sorted by country to have more deterministic results.
Tested on SQLite 3.34.0, PostgreSQL 14.3, GitHub upstream.
Most frequent for each GROUP BY group
MySQL: MySQL SELECT most frequent by group
PostgreSQL:
Get most common value for each value of another column in SQL
https://dba.stackexchange.com/questions/193307/find-most-frequent-values-for-a-given-column
SQLite: SQL query for finding the most frequent value of a grouped by value
SELECT TOP 20 WITH TIES COUNT(Counted_Column) AS Count, OtherColumn1,
OtherColumn2, OtherColumn3, OtherColumn4
FROM Table_or_View_Name
WHERE
(Date_Column >= '01/01/2023') AND
(Date_Column <= '03/01/2023') AND
(Counted_Column = 'Desired_Text')
GROUP BY OtherColumn1, OtherColumn2, OtherColumn3, OtherColumn4
ORDER BY COUNT(Counted_Column) DESC
20 can be changed to any desired number
WITH TIES allows all ties in the count to be displayed
Date range used if date/time column exists and can be modified to search a date range as desired
Counted_Column 'Desired_Text' can be modified to only count certain entries in that column
Works in INSQL for my instance
One way I like to use is:
select *<given_column>*,COUNT(*<given_column>*)as VAR1 from Table_Name
group by *<given_column>*
order by VAR1 desc
limit 1
I have a series of values in a database that I need to pull to create a line chart. Because i dont require high resolution I would like to resample the data by selecting every 5th row from the database.
SELECT *
FROM (
SELECT
#row := #row +1 AS rownum, [column name]
FROM (
SELECT #row :=0) r, [table name]
) ranked
WHERE rownum % [n] = 1
You could try mod 5 to get rows where the ID is multiple of 5. (Assuming you have some sort of ID column that's sequential.)
select * from table where table.id mod 5 = 0;
Since you said you're using MySQL, you can use user variables to create a continuous row numbering. You do have to put that in a derived table (subquery) though.
SET #x := 0;
SELECT *
FROM (SELECT (#x:=#x+1) AS x, mt.* FROM mytable mt ORDER BY RAND()) t
WHERE x MOD 5 = 0;
I added ORDER BY RAND() to get a pseudorandom sampling, instead of allowing every fifth row of the unordered table to be in the sample every time.
An anonymous user tried to edit this to change x MOD 5 = 0 to x MOD 5 = 1. I have changed it back to my original.
For the record, one can use any value between 0 and 4 in that condition, and there's no reason to prefer one value over another.
SET #a = 0;
SELECT * FROM t where (#a := #a + 1) % 2 = 0;
I had been looking for something like this. The answer of Taylor and Bill led me to improve upon their ideas.
table data1 has fields read_date, value
we want to select every 2d record from a query limited by a read_date range
the name of the derived table is arbitrary and here is called DT
query:
SET #row := 0;
SELECT * FROM ( SELECT #row := #row +1 AS rownum, read_date, value FROM data1
WHERE read_date>= 1279771200 AND read_date <= 1281844740 ) as DT WHERE MOD(rownum,2)=0
If you're using MariaDB 10.2, MySQL 8 or later, you can do this more efficiency, and I think more clearly, using common table expressions and window functions.
WITH ordering AS (
SELECT ROW_NUMBER() OVER (ORDER BY name) AS n, example.*
FROM example ORDER BY name
)
SELECT * FROM ordering WHERE MOD(n, 5) = 0;
Conceptually, this creates a temporary table with the contents of the example table ordered by the name field, adds an additional field called n which is the row number, and then fetches only those rows with numbers which are exactly divisible by 5, i.e. every 5th row. In practice, the database engine is often able to optimise this better than that. But even if it doesn't optimise it any further, I think it's clearer than using user variables iteratively as you had to in earlier versions of MySQL.
You can use this query,
set #n=2; <!-- nth row -->
select * from (SELECT t.*,
#rowid := #rowid + 1 AS ID
FROM TABLE t,
(SELECT #rowid := 0) dummy) A where A.ID mod #n = 0;
or you can replace n with your nth value
SELECT *
FROM (
SELECT #row := #row +1 AS rownum, posts.*
FROM (
SELECT #row :=0) r, posts
) ranked
WHERE rownum %3 = 1
where posts is my table.
If you don't require the row number in the result set you can simplify the query.
SELECT
[column name]
FROM
(SELECT #row:=0) temp,
[table name]
WHERE (#row:=#row + 1) % [n] = 1
Replace the following placeholders:
Replace [column name] with a list of columns you need to fetch.
Replace [table name] with the name of your table.
Replace [n] with a number. e.g. if you need every 5th row, replace it with 5
I have a table that is is sorted 1st by Reminder Date then ID
Table Looks like:
ID | remind_date
1 2011-01-23
2 2010-02-21
4 2011-04-04
5 2011-04-04
6 2009-05-04
I am using a PHP front end to move forward and back thur the records. I want to have forward and back buttons but i am running into a problem with the 2 reminder dates that are the same.
Just to note the ID's are NOT in order, they are here but in the actual database they are mixed up when sorting by reminder_date
The select statement i am using is: ($iid is the current record i am on)
SELECT id FROM myDB.reminders where remind_date > (SELECT remind_date FROM myDB.reminders where id=$iid) order by remind_date ASC LIMIT 1
So what happens when i get to the dates that are the same its skips over one because its asking for remind_date >.
If i use remind_date >= it returns the current record. My solution was then to use limit 2 and check via PHP to if the 1st record = my current ID, if it did use the next one. but what it there are 3 dates the same or 4 etc..
I also thought about using the ID field but since they are out of order i can't add in a ID > $iid.
Any ideas? it works great except for 2 dates that are the same.
You might be able to use this:
SELECT ID, remind_date
FROM
(
SELECT #prev_id := -1
) AS vars
STRAIGHT_JOIN
(
SELECT
ID,
remind_date,
#prev_id AS prev_id,
#prev_id := id
FROM myDB.reminders
ORDER BY remind_date, ID
) T1
WHERE prev_id = $iid
Here is a test of the above with your test data from your comment:
CREATE TABLE Table1 (ID INT NOT NULL, remind_date DATE NOT NULL);
INSERT INTO Table1 (ID, remind_date) VALUES
(45, '2011-01-14'),
(23, '2011-01-22'),
(48, '2011-01-23'),
(25, '2011-01-23'),
(63, '2011-02-19');
SELECT ID, remind_date
FROM
(
SELECT #prev_id := -1
) AS vars
STRAIGHT_JOIN
(
SELECT
ID,
remind_date,
#prev_id AS prev_id,
#prev_id := id
FROM table1
ORDER BY remind_date, ID
) T1
WHERE prev_id = 25
Result:
ID remind_date
48 2011-01-23
add a condition WHERE ID<>MY_LAST_ID. This can not work with triple and more same dates, so you can collect already taken ID's to array like (4,5,6) - see array_push(), implode it with "," to convert to a string (let's call it YOUR_IDS_STRING) and add to your query:
WHERE id NOT IN( YOUR_IDS_STRING )
And after each query make check, does date has changed and if it does - you can unset your array and start from begining (this is not neccesary, but gives you more performance, because YOUR_ID_STRING will be only that long as is need).
If your page is refreshing between queries, maybe try to push YOUR_ID_STRING in session variable, _GET or cookies, and simply concat next id's by operator .=
I used the code provided by Mark Byers and with small changes I adapted it to navigate in opposite directions (and to pass other columns too, not only the date and ID):
$results = $mysqli->query("SELECT * FROM (SELECT #prev_id := -1) AS vars STRAIGHT_JOIN (SELECT *, #prev_id AS prev_id, #prev_id := ID FROM my_table ORDER BY data, ID) T1 WHERE prev_id = ".$ID);
$results = $mysqli->query("SELECT * FROM (SELECT #next_id := 1) AS vars STRAIGHT_JOIN (SELECT *, #next_id AS next_id, #next_id := ID FROM my_table ORDER BY data DESC, ID DESC) T1 WHERE next_id = ".$ID);
I tested it on duplicate dates and it navigates well trough a list of records displayed with:
$results = $mysqli->query("SELECT * FROM my_table ORDER BY data DESC, ID DESC");