I have table:
+-----+------------+--------------+
| id | title | numbers |
| 2 | Title 1 | 2,8,5 |
| 3 | Title 2 | 50,7,9,4 |
+-----+------------+--------------+
Is it possible to sort within the column? In this case in column numbers.
I need to output ordered numbers column as follows:
+-----+------------+--------------+
| id | title | numbers |
| 2 | Title 1 | 2,5,8 |
| 3 | Title 2 | 4,7,9,50 |
+-----+------------+--------------+
Something like:
SELECT id, title, SORT_FUNC(numbers) from table
I was looking for some function in MySQL documentation, but I found nothing.
It is possible, but not really a good idea.
As an example, you can split a comma separated list up by generating a range of numbers and using that with SUBSTRING_INDEX to get each element. However the range of numbers needs to be as big as the max number of delimited values.
You could then use GROUP_CONCAT to join the list back together in the right order. Note that the order will be different depending on whether you have cast the split up values as numbers / integers or left them as strings.
SELECT id, title, GROUP_CONCAT(aNumber ORDER BY aNumber)
FROM
(
SELECT id, title, CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(numbers, ',', tens.acnt * 10 + units.acnt + 1), ',', -1) AS UNSIGNED) AS aNumber
FROM some_table
CROSS JOIN
(SELECT 0 AS acnt UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) units
CROSS JOIN
(SELECT 0 AS acnt UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) tens
WHERE LENGTH(numbers) - LENGTH(REPLACE(numbers, ',', '')) >= tens.acnt * 10 + units.acnt
) sub0
GROUP BY id, title;
Demonstrated here on SQL fiddle (if SQL fiddle decides to work):-
http://www.sqlfiddle.com/#!9/c9703ee/4
First select is casting the values as integers to sort them numerically, 2nd one isn't casting them but just leaving them as strings, hence the sort order is different.
Related
While every question I found is talking about removing duplicates I need these duplicates.
Let's say my database is
+-------+-----------+
| ID | letter |
+-------+-----------+
| 1 | A |
| 2 | B |
| 4 | Z |
+-------+-----------+
I need to query a person name so let say the name is "ABA" when I query like this
select * from letters where letter = 'A' or letter = 'B' or letter = 'A'
My result will be
+-------+-----------+
| ID | letter |
+-------+-----------+
| 1 | A |
| 2 | B |
+-------+-----------+
I want the output will include the 3rd letter as a separate row.
+-------+-----------+
| ID | letter |
+-------+-----------+
| 1 | A |
| 2 | B |
| 3 | A |
+-------+-----------+
Maybe I don't know the right term but I didn't find even one answer that give me half a solution.
there is one entry but can I can the entry again? if I query for "nina" get the full name and not just "nia"
Original answer (and recommended approach)
Use a recursive query to convert the name into rows with one letter each. Then join with your letters table.
with recursive word_letters (word, pos, letter) as
(
select #name, 1, substr(#name, 1, 1)
union all
select word, pos + 1, substr(word, pos + 1, 1)
from word_letters
where pos < length(word)
)
select letters.*
from word_letters
join letters on letters.letter = word_letters.letter
order by word_letters.pos;
Demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=6547c21d2dd9223270615047d46d9783
UPDATE: Workaround for old MySQL versions
Build a table of positions (numbers) large enough to cover the longest word. Then join the word in order to get the position for each letter in it. Then join your other table.
select letters.*
from
(
select hundreds.digit * 100 + tens.digit * 10 + units.digit + 1 as pos
from (select 0 as digit union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) units
cross join (select 0 as digit union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) tens
cross join (select 0 as digit union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) hundreds
) positions
join (select #name as word) w on length(w.word) >= positions.pos
join letters on letters.letter = substr(w.word, positions.pos, 1)
order by positions.pos;
Demo: https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=db3762d5705ce5eb77e628c4d4058485
You can do it by using any of the below queries. For your reference visit the link below to test the output.
http://sqlfiddle.com/#!9/c5e9f3/1
The table created and the populated data is as below
create table tbl(ID int,letter varchar(2));
insert into tbl(ID,letter) values(1,'A');
insert into tbl(ID,letter) values(2,'B');
insert into tbl(ID,letter) values(3,'A');
insert into tbl(ID,letter) values(4,'Z');
insert into tbl(ID,letter) values(5,'C');
and now the query
select * from tbl where letter in ('A','B');
or
select * from tbl where letter ='A' or letter='B';
I have table:
+-----+------------+--------------+
| id | title | numbers |
| 2 | Title 1 | 2,8,5 |
| 3 | Title 2 | 50,7,9,4 |
+-----+------------+--------------+
Is it possible to sort within the column? In this case in column numbers.
I need to output ordered numbers column as follows:
+-----+------------+--------------+
| id | title | numbers |
| 2 | Title 1 | 2,5,8 |
| 3 | Title 2 | 4,7,9,50 |
+-----+------------+--------------+
Something like:
SELECT id, title, SORT_FUNC(numbers) from table
I was looking for some function in MySQL documentation, but I found nothing.
It is possible, but not really a good idea.
As an example, you can split a comma separated list up by generating a range of numbers and using that with SUBSTRING_INDEX to get each element. However the range of numbers needs to be as big as the max number of delimited values.
You could then use GROUP_CONCAT to join the list back together in the right order. Note that the order will be different depending on whether you have cast the split up values as numbers / integers or left them as strings.
SELECT id, title, GROUP_CONCAT(aNumber ORDER BY aNumber)
FROM
(
SELECT id, title, CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(numbers, ',', tens.acnt * 10 + units.acnt + 1), ',', -1) AS UNSIGNED) AS aNumber
FROM some_table
CROSS JOIN
(SELECT 0 AS acnt UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) units
CROSS JOIN
(SELECT 0 AS acnt UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) tens
WHERE LENGTH(numbers) - LENGTH(REPLACE(numbers, ',', '')) >= tens.acnt * 10 + units.acnt
) sub0
GROUP BY id, title;
Demonstrated here on SQL fiddle (if SQL fiddle decides to work):-
http://www.sqlfiddle.com/#!9/c9703ee/4
First select is casting the values as integers to sort them numerically, 2nd one isn't casting them but just leaving them as strings, hence the sort order is different.
I am getting number of visits every day for generating a chart. Even when there are zero records, I want to get the record with count 0.
I am planning to create a table which will contain every day, and when fetching - data will join with this table and get count of the records from visit table. Is there any other way to do the same in mySQL?
Visit Table with Sample Data
Date | ........
----------------------
01/11/2014 | --------
03/11/2014 | --------
I want results even for 02/11/2014 with count 0. If I group by date - I will get count only when records exists on a particular date.
I'll try to read in between lines of your question... Sort of game where I write the question and the answer :-/
You have a table (my_stats) holding two fields, one is the date (my_date) the other is a integer (my_counter).
By some mean, you will need a table holding a list of all dates you want to use in your output.
This could be done with a temp table... (but not all hosting solution will allow you this) the other is to build it up on the fly, using a view or a stored procedure.
Then you will LEFT JOIN this table/view/stored procedure/etc... to your table my_visits based on the date field.
This will output you all dates, and when there won't be a match in mour my_visits you'll have a NULL value. ( IFNULL(my_visits.my_counter, 0) will give you a 0 (zero) when there is no matching value.
inspiration:
Get a list of dates between two dates +
How to get list of dates between two dates in mysql select query and a nice solution here that needs no loops, procedures, or temp tables generate days from date range
Based on that last link, here we go...
first a sample table
DROP TABLE IF EXISTS `my_stats`;
CREATE TABLE IF NOT EXISTS `my_stats` (
`my_date` date NOT NULL,
`my_counter` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
INSERT INTO `my_stats` (`my_date`, `my_counter`) VALUES
('2017-11-01', 2),
('2017-11-02', 3),
('2017-11-03', 5),
('2017-11-05', 3),
('2017-11-07', 7);
And now a working exemple BETWEEN '2017-11-01' AND '2017-11-09'
SELECT date_range.date AS the_date,
IFNULL(my_stats.my_counter, 0) AS the_counter
FROM (
SELECT a.date
FROM (
SELECT Curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) day
AS date
FROM (
SELECT 0 AS a
UNION ALL
SELECT 1
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4
UNION ALL
SELECT 5
UNION ALL
SELECT 6
UNION ALL
SELECT 7
UNION ALL
SELECT 8
UNION ALL
SELECT 9
) AS a
CROSS JOIN (
SELECT 0 AS a
UNION ALL
SELECT 1
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4
UNION ALL
SELECT 5
UNION ALL
SELECT 6
UNION ALL
SELECT 7
UNION ALL
SELECT 8
UNION ALL
SELECT 9
) AS b
CROSS JOIN (
SELECT 0 AS a
UNION ALL
SELECT 1
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4
UNION ALL
SELECT 5
UNION ALL
SELECT 6
UNION ALL
SELECT 7
UNION ALL
SELECT 8
UNION ALL
SELECT 9
) AS c
) AS a
WHERE a.date BETWEEN '2017-11-01' AND '2017-11-09'
) AS date_range
LEFT JOIN my_stats
ON( date_range.date = my_stats.my_date )
ORDER BY the_date ASC
Output
+------------+-------------+
| the_date | the_counter |
+------------+-------------+
| 2017-11-01 | 2 |
| 2017-11-02 | 3 |
| 2017-11-03 | 5 |
| 2017-11-04 | 0 |
| 2017-11-05 | 3 |
| 2017-11-06 | 0 |
| 2017-11-07 | 7 |
| 2017-11-08 | 0 |
| 2017-11-09 | 0 |
+------------+-------------+
I have a select statement which fetches a value field on an ID
177,175,173,178,179
What is returned is anywhere from null, a single entry, to multiple comma delimited values in this field.
How can I use SQL to return 1 row for each value based on this comma delimited field?
As it has already been mentioned in comments consider to normalize your data to be able to normally maintain and query your data.
In the meantime you achieve your goal in pure SQL with the help of tally(number) table which you can create like this
CREATE TABLE tally (n INT NOT NULL PRIMARY KEY);
INSERT INTO tally
SELECT a.N + b.N * 10 + 1 n
FROM
(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) a
,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) b
ORDER BY n;
Now your might look like
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(value, ',', n), ',', -1) value
FROM table1 CROSS JOIN tally
WHERE id = 1
AND n <= 1 + (LENGTH(value) - LENGTH(REPLACE(value, ',', '')))
Output:
| VALUE |
|-------|
| 177 |
| 175 |
| 173 |
| 178 |
| 179 |
Here is SQLFiddle demo
I have a table like this...
mytbl(names)values('ron'),('sam'),('john'),('tony')
Now,I want to see the words from the names column where each word will be sorted by letters in alphabetically ascending order.I'm trying to clarify my requirements more specifically..
I want like this
'ron' will be sorted as 'nor'
'sam' will be sorted as 'ams'
'john' will be sorted as 'hjno'
'tony' will be sorted as 'noty'
and I want see exactly those sorted values as my output.How shall I get that...how should I select from the table to get such output?
Please solve my problem.thanks in advance.
You can do that in pure SQL with the help of a tally(numbers) table which you can easily create and populate like this
CREATE TABLE tally(n INT NOT NULL PRIMARY KEY);
INSERT INTO tally
SELECT a.N + b.N * 10 + 1 n
FROM
(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) a
,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) b
ORDER BY n
Now the query
SELECT names, GROUP_CONCAT(letter ORDER BY letter SEPARATOR '') sorted_name
FROM
(
SELECT names, MID(names, n.n, 1) letter
FROM mytbl e JOIN tally n
ON n.n <= CHAR_LENGTH(names)
) q
GROUP BY names
Output:
| NAMES | SORTED_NAME |
|-------|-------------|
| john | hjno |
| ron | nor |
| sam | ams |
| tony | noty |
Here is SQLFiddle demo