I need a query that requires me to return something like below.
data table
--------------------------------
product name | quantity
apple | 3
egg | 2
query expected result
--------------------------------
product name
apple
apple
apple
egg
egg
Thanks for feedback
UPDATE #1
ok my bad the question is unclear. I want to display my result by looping my product by quantity. Probably my expected result on quantity makes some confusion. Therefore, is that possible to loop my record by quantity in mysql?
If you really need this in SQL you can leverage a tally(numbers) table, which you can create and populate with values 1-100 like so
CREATE TABLE tally(n INT NOT NULL AUTO_INCREMENT PRIMARY KEY);
INSERT INTO tally (n)
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;
Then your query would look
SELECT product_name
FROM data_table d JOIN tally t
ON t.n <= d.quantity
Note: Make sure that you have MAX(n) value in your tally table >= MAX(quantity) in your data_table for this to work properly
Output:
+--------------+
| product_name |
+--------------+
| apple |
| apple |
| apple |
| egg |
| egg |
+--------------+
Here is SQLFiddle demo
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 following table.
Sales:
id quantity price_charged
------------------------------
101 2 100
102 3 300
103 1 120
I want to select the records such that it repeat Rows N time according to quantity column value.
So I need following results
id quantity price_charged
--------------------------------
101 1 50
101 1 50
102 1 100
102 1 100
102 1 100
103 1 120
I think, it is better to resolve not with query(SQL).
There is some generation feature, but its performance is poor.
You have to change your model(store always 1 quantity), or process it in backend(java/c/stb.)
Select id,
1 as quantity,
price_charged
from table_name t
JOIN
(SELECT e*10000+d*1000+c*100+b*10+a n FROM
(select 0 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) t1,
(select 0 b 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) t2,
(select 0 c 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) t3,
(select 0 d 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) t4,
(select 0 e 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) t5) counter
ON (counter.n<=t.quantity)
The joined subquery reapeted numbers from 0 to 99999 it is the burn it maximum for quantity. The join repeat by the counter 0... quantity-1 values.
I was able to come up with a solution for my problem after referring an answer for how to generate series in mysql. Here is the link.
SELECT
sal.id,
1 as quantity, sal.quantity as originalQty,
sal.price_charged/sal.quantity
FROM
(SELECT
#num := #num + 1 AS count
FROM
sales, -- this can be any table but it should have row count
-- more than what we expect the max value of Sales.quantity column
(SELECT #num := 0) num
LIMIT
100) ctr
JOIN sales sal
ON sal.quantity >= ctr.count
order by id;
If you are lucky enough to be running MySQL 8.0, you can use a recursive CTE to solve this problem. This is an elegant solution that does not require creating a list of numbers of using variables.
Consider this query:
WITH RECURSIVE cte AS (
SELECT 1 n, id, quantity, price_charged FROM sales
UNION ALL
SELECT n + 1, id, quantity, price_charged FROM cte WHERE n < quantity
)
SELECT id, quantity, price_charged/quantity quantity
FROM cte
ORDER BY id;
In this DB Fiddle with your sample data, the query returns:
| id | quantity | quantity |
| --- | -------- | -------- |
| 101 | 2 | 50 |
| 101 | 2 | 50 |
| 102 | 3 | 100 |
| 102 | 3 | 100 |
| 102 | 3 | 100 |
| 103 | 1 | 120 |
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 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
I have a MySQL table that looks something like this:
|---ID---|---COUNTER---|
| 1 | 2 |
| 2 | 6 |
| 3 | 1 |
| 5 | 9 |
| 6 | 10 |
I'm looking for a SELECT statement that returns ID's and their COUNTER. The table only have ID's such as: 1,2,3,5,6. Is there a statement where you say: I want ID's 1 to 10 even if they doesn't exist in the table, and if the ID doesn't exist, return the ID anyway with the COUNTER value 0. For example:
|---ID---|---COUNTER---|
| 1 | 2 |
| 2 | 6 |
| 3 | 1 |
| 4 | 0 |
| 5 | 9 |
| 6 | 10 |
| 7 | 0 |
| 8 | 0 |
| 9 | 0 |
| 10 | 0 |
Do I have to create a SELECT statement that contains NOT EXIST parameters?
Thanks in advance, Steve-O
Without creating a temp table:
select t.num as id, coalesce(yt.counter, 0)
from your_table yt
right join (
select 1 as num union select 2 union select 3 union select 4 union select 5 union
select 6 union select 7 union select 8 union select 9 union select 10
) t on yt.id = t.num
order by t.num
and bit more general:
select t.num as id, coalesce(yt.counter, 0)
from your_table yt
right join (
select t1.num + t2.num * 10 + t3.num * 100 as num
from (
select 1 as num union select 2 union select 3 union select 4 union select 5 union
select 6 union select 7 union select 8 union select 9 union select 0
) t1
cross join (
select 1 as num union select 2 union select 3 union select 4 union select 5 union
select 6 union select 7 union select 8 union select 9 union select 0
) t2
cross join (
select 1 as num union select 2 union select 3 union select 4 union select 5 union
select 6 union select 7 union select 8 union select 9 union select 0
) t3
) t on yt.id = t.num
where t.num between (select min(id) from your_table) and (select max(id) from your_table)
You can define limit by yourself here I've used min and max of id value from your_table.
It's not very robust, but if you created a temporary table with the ID's you wanted in it, you could then left join to your table containing ID and Counter which would include all the values:
Declare #tempidtable as table ( imaginaryid int )
insert into #tempidtable ( imaginaryid ) values ( 1 )
insert into #tempidtable ( imaginaryid ) values ( 2 )
insert into #tempidtable ( imaginaryid ) values ( 3 )
select
#temptable.imaginaryid,
ISNULL(yourothertable.counter, 0)
from #tempidtable
left join yourothertable
on #tempidtable.imaginaryid = yourothertable.id
As Tomek says you could loop over the inserts to make it easier to maintain, or possible store the ids you want as a base in another table, using this as the basis for the join rather than a temp table.
Create a table with all possible ID's:
create table Numbers (nr int primary key);
declare i int default 1;
while i < 100000 do
insert into Numbers (nr) values (i);
set i = i + 1;
end while;
Then you can use left join to return all numbers:
select n.NR
, c.Counter
from Numbers n
left join
Counters c
on c.ID = n.NR
You can use left join to solve your issue. Read more about left join here
I think you will have to create (generate in loop) temporary table with the complete sequence of numbers from 1 to N (where N is the MAX(Id) of counted table). Then do left join to that table and apply GROUP BY clause.
You need the range of integers to do an outer join with your table based on ID. Generating a range of integers is dependent on the SQL vendor if you do not want to use a temporary table. See SQL SELECT to get the first N positive integers for hints on how to do this based on your SQL vendor.