What is the PostgreSQL's generate_series() equivalent in MySQL?
How to convert this query to MySQL?
select substr('some-string', generate_series(1, char_length('some-string')))
Sample output from PostgreSQL:
some-string
ome-string
me-string
e-string
-string
string
tring
ring
ing
ng
g
select generate_series(1, char_length('some-string'))
1
2
3
4
5
6
7
8
9
10
11
Final solution:
CREATE TABLE `numberlist` (
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
INSERT INTO `numberlist` values(null)
(repeat the above query the maximum string you need)
SELECT substr('somestring', id)
FROM numberlist
WHERE id <= character_length('somestring')
Here is the concept, but I don't have mySQL installed on this box. You will need to create a table of integers, using AUTO INCREMENT. A table of numbers is generally a handy table to have available in a database, and would only need be created once
create table NumberList (id MEDIUMINT NOT NULL AUTO_INCREMENT,fill char(1))
declare #x INT
set #x=0
while #x < 20
begin
insert into numberList values(null)
Set #x = #x+1
end
Then, join this table as shown below using the LIMIT clause
select substr('somestring',id)
from numberlist
limit len('somestring')
I wrote this in SQL server, but it shouldn't be too difficult to convert to mySQL...
The code below SHOULD work in mySQL
DECLARE xx INT DEFAULT 0;
WHILE xx < 20 DO
insert into numberList values(null)
SET xx = xx + 1;
END WHILE;
Related
I am using MySQL. I want to insert value's result from groupby of datetime to specific column (using where, maybe). Let say:
I have two tables (a, b). In table a, I want to get how many total records during a hour (which I have datetime column), then the result will insert into table b, but in specific ID (there is already exist ID's value).
This is my error code:
INSERT INTO b(value)
WHERE ID=15
SELECT DAY COUNT(*)
FROM a
WHERE date >= '2015-09-19 00:00:00' AND date < '2015-09-19 00:59:59'
GROUP BY DAY(date),HOUR(date);";
Is that possible I make a query from this case?
Thank you very much for any reply!
Schema
create table tA
( id int auto_increment primary key,
theDate datetime not null,
-- other stuff
key(theDate) -- make it snappy fast
);
create table tB
( myId int primary key, -- by definition PK is not null
someCol int not null
);
-- truncate table tA;
-- truncate table tB;
insert tA(theDate) values
('2015-09-19'),
('2015-09-19 00:24:21'),
('2015-09-19 07:24:21'),
('2015-09-20 00:00:00');
insert tB(myId,someCol) values (15,-1); -- (-1) just for the heck of it
insert tB(myId,someCol) values (16,-1); -- (-1) just for the heck of it
The Query
update tB
set someCol=(select count(*) from tA where theDate between '2015-09-19 00:00:00' and '2015-09-19 00:59:59')
where tB.myId=15;
The Results
select * from tB;
+------+---------+
| myId | someCol |
+------+---------+
| 15 | 2 |
| 16 | -1 |
+------+---------+
only myId=15 is touched.
Table Product
id name price quantity total
1 food 50 1 50
2 drink 20 2 40
3 dress 100 3 300
How do I declare a table that has a column that is the product of two columns?
I have this code:
CREATE TABLE [dbo].[Orders] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[ProductName] NCHAR (70) NULL,
[Price] INT NULL,
[Quantity] INT NULL,
[Total] INT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
Sounds like you want a VIEW.
Their example is exactly what you're describing
mysql> CREATE TABLE t (qty INT, price INT);
mysql> INSERT INTO t VALUES(3, 50);
mysql> CREATE VIEW v AS SELECT qty, price, qty*price AS value FROM t;
mysql> SELECT * FROM v;
+------+-------+-------+
| qty | price | value |
+------+-------+-------+
| 3 | 50 | 150 |
+------+-------+-------+
You can try this mate:
DROP TRIGGER IF EXISTS trg_product_total;
DELIMITER //
CREATE TRIGGER trg_product_total AFTER INSERT ON product
FOR EACH ROW BEGIN
SET #price = NULL, #quantity = NULL;
SELECT price INTO #price FROM product
WHERE id = NEW.id;
SELECT quantity INTO #quantity
WHERE id = NEW.id;
UPDATE product SET total = #price * #quantity
WHERE id = NEW.id;
END;
You can use this kind of approach if you don't really want to process the product.total before inserting it into the DB.
The Trigger will execute each time a new record is added into the table, wherein the expected insert for the total column is either 'NULL' or '0' depending on your default value.
But I think it would be better if you calculate it before the insert.
The flow would be like:
Application side
1. get price and quantity for the product
2. calculate for the total
3. insert values into the query
4. execute query
In case you want to learn more about MySQL Trigger: Link
Also, PHP Transaction: Link
I have this mysql table built like this:
CREATE TABLE `posts` (
`post_id` INT(10) NOT NULL AUTO_INCREMENT,
`post_user_id` INT(10) NOT NULL DEFAULT '0',
`gen_id` INT(10) NOT NULL DEFAULT '0',
PRIMARY KEY (`post_user_id`, `post_id`)
)
COLLATE='utf8_general_ci'
ENGINE=MyISAM;
When I do:
insert into posts (post_user_id) values (1);
insert into posts (post_user_id) values (1);
insert into posts (post_user_id) values (2);
insert into posts (post_user_id) values (1);
select * from posts;
I get:
post_id | post_user_id | gen_id
1 1 0
2 1 0
1 2 0
3 1 0
A unique post_id is generated for each unique user.
I need the gen_id column to be 1 2 3 4 5 6 etc. How can I increment this column when I do an insert. I tried the one below, but it won't work. What's the right way to do this?
insert into posts (post_user_id,gen_id) values (1,select max(gen_id)+1 from posts);
//Select the highest gen_id and add 1 to it.
Try this:
INSERT INTO posts (post_user_id,gen_id)
SELECT 1, MAX(gen_id)+1 FROM posts;
Use a TRIGGER on your table. This sample code can get you started:
DELIMITER //
CREATE TRIGGER ai_trigger_name AFTER INSERT ON posts
FOR EACH ROW
BEGIN
UPDATE posts
SET gen_id = (SELECT MAX(gen_id) FROM posts) + 1
WHERE post_id = LAST_INSERT_ID()
LIMIT 1;
END;//
DELIMITER ;
For my case the first number to increment was null. I resolve with
IFNULL(MAX(number), 0) + 1
or better the query became
SELECT IFNULL(MAX(number), 0) + 1 FROM mytable;
Here is the table "Autos" and the data that it contains to begin with:
AutoID | Year | Make | Model | Color |Seq
1 | 2012 | Jeep |Liberty| Black | 1
2 | 2013 | BMW | 330XI | Blue | 2
The AutoID column is an auto incrementing column so it is not necessary to include it in the insert statement.
The rest of the columns are varchars except for the Seq column which is an integer column/field.
If you want to make it so that when you insert the next row into the table and the Seq column auto increments to the # 3 you need to write your query as follows:
INSERT INTO Autos
(
Seq,
Year,
Make,
Model,
Color,
)
Values
(
(SELECT MAX(Seq) FROM Autos) + 1, --this increments the Seq column
2013,'Mercedes','S550','Black');
The reason that I put the Seq column first is to ensure that it will work correctly... it does not matter where you put it, but better safe than sorry.
The Seq column should now have a value of 3 along with the added values for the rest of that row in the database.
The way that I intended that to be displayed did not happen...so I will start from the beginning: First I created a table.
create table Cars (
AutoID int identity (1,1) Primary Key,
Year int,
Make varchar (25),
Model varchar (25),
TrimLevel varchar (30),
Color varchar (30),
CreatedDate date,
Seq int
)
Secondly I inserted some dummy values
insert into Cars values (
2013,'Ford' ,'Explorer','XLT','Brown',GETDATE(),1),
(2011,'Hyundai' ,'Sante Fe','SE','White',GETDATE(),2),
(2009,'Jeep' ,'Liberty','Jet','Blue',GETDATE(),3),
(2005,'BMW' ,'325','','Green',GETDATE(),4),
(2008,'Chevy' ,'HHR','SS','Red',GETDATE(),5);
When the insertion is complete you should have 5 rows of data.
Since the Seq column is not an auto increment column and you want to ensure that the next Seq's row of data is automatically incremented to the # 6 and its subsequent rows are incremented as well you would need to write the following code:
INSERT INTO Cars
(
Seq,
Year,
color,
Make,
Model,
TrimLevel,
CreatedDate
)
Values
(
(SELECT MAX(Seq) FROM Cars) + 1,
2013,'Black','Mercedes','A550','AMG',GETDATE());
I have run this insert statement many times using different data just to make sure that it works correctly....hopefully this helps!
Hi For many days I have been working on this problem in MySQL, however I can not figure it out. Do any of you have suggestions?
Basically, I have a category table with domains like: id, name (name of category), and parent (id of parent of the category).
Example Data:
1 Fruit 0
2 Apple 1
3 pear 1
4 FujiApple 2
5 AusApple 2
6 SydneyAPPLE 5
....
There are many levels, possibly more than 3 levels. I want to create an sql query that groups the datas according to he hierarchy: parent > child > grandchild > etc.
It should output the tree structure, as follows:
1 Fruit 0
^ 2 Apple 1
^ 4 FujiApple 2
- 5 AusApple 2
^ 6 SydneyApple 5
- 3 pear 1
Can I do this using a single SQL query? The alternative, which I tried and does work, is the following:
SELECT * FROM category WHERE parent=0
After this, I loop through the data again, and select the rows where parent=id. This seems like a bad solution. Because it is mySQL, CTEs cannot be used.
You can do it in a single call from php to mysql if you use a stored procedure:
Example calls
mysql> call category_hier(1);
+--------+---------------+---------------+----------------------+-------+
| cat_id | category_name | parent_cat_id | parent_category_name | depth |
+--------+---------------+---------------+----------------------+-------+
| 1 | Location | NULL | NULL | 0 |
| 3 | USA | 1 | Location | 1 |
| 4 | Illinois | 3 | USA | 2 |
| 5 | Chicago | 3 | USA | 2 |
+--------+---------------+---------------+----------------------+-------+
4 rows in set (0.00 sec)
$sql = sprintf("call category_hier(%d)", $id);
Hope this helps :)
Full script
Test table structure:
drop table if exists categories;
create table categories
(
cat_id smallint unsigned not null auto_increment primary key,
name varchar(255) not null,
parent_cat_id smallint unsigned null,
key (parent_cat_id)
)
engine = innodb;
Test data:
insert into categories (name, parent_cat_id) values
('Location',null),
('USA',1),
('Illinois',2),
('Chicago',2),
('Color',null),
('Black',3),
('Red',3);
Procedure:
drop procedure if exists category_hier;
delimiter #
create procedure category_hier
(
in p_cat_id smallint unsigned
)
begin
declare v_done tinyint unsigned default 0;
declare v_depth smallint unsigned default 0;
create temporary table hier(
parent_cat_id smallint unsigned,
cat_id smallint unsigned,
depth smallint unsigned default 0
)engine = memory;
insert into hier select parent_cat_id, cat_id, v_depth from categories where cat_id = p_cat_id;
/* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */
create temporary table tmp engine=memory select * from hier;
while not v_done do
if exists( select 1 from categories p inner join hier on p.parent_cat_id = hier.cat_id and hier.depth = v_depth) then
insert into hier
select p.parent_cat_id, p.cat_id, v_depth + 1 from categories p
inner join tmp on p.parent_cat_id = tmp.cat_id and tmp.depth = v_depth;
set v_depth = v_depth + 1;
truncate table tmp;
insert into tmp select * from hier where depth = v_depth;
else
set v_done = 1;
end if;
end while;
select
p.cat_id,
p.name as category_name,
b.cat_id as parent_cat_id,
b.name as parent_category_name,
hier.depth
from
hier
inner join categories p on hier.cat_id = p.cat_id
left outer join categories b on hier.parent_cat_id = b.cat_id
order by
hier.depth, hier.cat_id;
drop temporary table if exists hier;
drop temporary table if exists tmp;
end #
Test runs:
delimiter ;
call category_hier(1);
call category_hier(2);
Some performance testing using Yahoo geoplanet places data
drop table if exists geoplanet_places;
create table geoplanet_places
(
woe_id int unsigned not null,
iso_code varchar(3) not null,
name varchar(255) not null,
lang varchar(8) not null,
place_type varchar(32) not null,
parent_woe_id int unsigned not null,
primary key (woe_id),
key (parent_woe_id)
)
engine=innodb;
mysql> select count(*) from geoplanet_places;
+----------+
| count(*) |
+----------+
| 5653967 |
+----------+
so that's 5.6 million rows (places) in the table let's see how the adjacency list implementation/stored procedure called from php handles that.
1 records fetched with max depth 0 in 0.001921 secs
250 records fetched with max depth 1 in 0.004883 secs
515 records fetched with max depth 1 in 0.006552 secs
822 records fetched with max depth 1 in 0.009568 secs
918 records fetched with max depth 1 in 0.009689 secs
1346 records fetched with max depth 1 in 0.040453 secs
5901 records fetched with max depth 2 in 0.219246 secs
6817 records fetched with max depth 1 in 0.152841 secs
8621 records fetched with max depth 3 in 0.096665 secs
18098 records fetched with max depth 3 in 0.580223 secs
238007 records fetched with max depth 4 in 2.003213 secs
Overall i'm pretty pleased with those cold runtimes as I wouldn't even begin to consider returning tens of thousands of rows of data to my front end but would rather build the tree dynamically fetching only several levels per call. Oh and just incase you were thinking innodb is slower than myisam - the myisam implementation I tested was twice as slow in all counts.
More stuff here : http://pastie.org/1672733
Hope this helps :)
There are two common ways of storing hierarchical data in an RDBMS: adjacency lists (which you are using) and nested sets. There is a very good write-up about these alternatives in Managing Hierarchical Data in MySQL. You can only do what you want in a single query with the nested set model. However, the nested set model makes it more work to update the hierarchical structure, so you need to consider the trade-offs depending on your operational requirements.
You can't achieve this using a single query. Your hierarchical data model is ineffective in this case. I suggest you try two other ways of storing hierarchical data in a database: the MPTT model or the "lineage" model. Using either of those models allows you to do the select you want in a single go.
Here is an article with further details: http://articles.sitepoint.com/article/hierarchical-data-database
The linear way:
I am using a ugly function to create a tree in a simple string field.
/ topic title
/001 message 1
/002 message 2
/002/001 reply to message 2
/002/001/001/ reply to reply
/003 message 3
etc...
the table can be used to select all the rows in the tree order with a simple SQL Query:
select * from morum_messages where m_topic=1234 order by m_linear asc
INSERT is just select the parent linear (and children) and calculate the string as needed.
select M_LINEAR FROM forum_messages WHERE m_topic = 1234 and M_LINEAR LIKE '{0}/___' ORDER BY M_LINEAR DESC limit 0,1
/* {0} - m_linear of the parent message*/
DELETE is simple as delete the message, or delete by linear all replies of the parent one.
I having a referral table like below.
> id referredByID referrerID
>
> 1001 1 2
>
> 1002 2 3
>
> 1003 2 4
>
> 1004 5 7
From the above table structure i need to get the users whom i referred and the users whom are referred by their referrals.
For Example:
I am referredByID-1
I referred the ID - 2
Now the ID - 2 referred ID -3
And in the same case ID-2 referred ID - 4
Now my output needs to be look like:
Referrals Done By Me:
id - 2
id - 3
id - 4
How can this be done using MYSQL.
Any help will be appreciated.. Thanks in advance...
I think I got everything the right way round but your naming conventions confused me so you'd better check everything.
If I call the following stored procedure:
call referrals_hier(1);
I get the following results:
+--------------+------------+-------+
| referredByID | referrerID | depth |
+--------------+------------+-------+
| 1 | 2 | 0 |
| 2 | 3 | 1 |
| 2 | 4 | 1 |
+--------------+------------+-------+
3 rows in set (0.00 sec)
full script here: http://pastie.org/1466596
Stored procedure
drop table if exists referrals;
create table referrals
(
id smallint unsigned not null primary key,
referrerID smallint unsigned not null,
referredByID smallint unsigned null,
key (referredByID)
)
engine = innodb;
insert into referrals (id, referredByID, referrerID) values
(1001,1,2),(1002,2,3),(1003,2,4),(1004,5,7);
drop procedure if exists referrals_hier;
delimiter #
create procedure referrals_hier
(
in p_refID smallint unsigned
)
begin
declare v_done tinyint unsigned default(0);
declare v_dpth smallint unsigned default(0);
create temporary table hier(
referredByID smallint unsigned,
referrerID smallint unsigned,
depth smallint unsigned
)engine = memory;
insert into hier select referredByID, referrerID, v_dpth from referrals where referredByID = p_refID;
/* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */
create temporary table tmp engine=memory select * from hier;
while not v_done do
if exists( select 1 from referrals e inner join hier on e.referredByID = hier.referrerID and hier.depth = v_dpth) then
insert into hier select e.referredByID, e.referrerID, v_dpth + 1
from referrals e inner join tmp on e.referredByID = tmp.referrerID and tmp.depth = v_dpth;
set v_dpth = v_dpth + 1;
truncate table tmp;
insert into tmp select * from hier where depth = v_dpth;
else
set v_done = 1;
end if;
end while;
select * from hier order by depth;
drop temporary table if exists hier;
drop temporary table if exists tmp;
end #
delimiter ;
-- call this sproc from your php
call referrals_hier(1);
Hope this helps :)
There are two ways, both described here with examples:
http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/
These are the cases where MySQL's lack of support for recursive common table expressions really hurts.
If you have an upper limit on the levels, then you might be able to do this with several self joins:
SELECT l1.referredID, l2.referredID, ...
FROM your_table l1
LEFT JOIN your_table l2 ON l2.referredByID = l1.referredID
LEFT JOIN your_table l3 ON l3.referredByID = l2.referredID
LEFT JOIN your_table l4 ON l4.referredByID = l3.referredID
... (you get the picture)
Now as you can see this gets ugly when having more levels and also will not perform very well for larger sets.
If you cannot change your table design then I would suggest to make a good guess on the maximum depth that you can have and create a view that will retrieve all levels. At least that makes it easier in the application or for ad-hoc queries.
On top of that (huge self join) view, you can also build another view that returns each level as its own row. But that will be even slower.
But as long as you deal with MySQL the best thing to do is to change the table design to use the nested set model which is described in the link to the MySQL manual that Anonymous87 has posted.