Perform a INSERT .. SELECT in MySQL with derived tables - mysql

I'm trying to insert the results from a join query into another table.
INSERT INTO temp(
SELECT b.id, b.number, b.attempt FROM(
SELECT number FROM duplicate_numbers)a
JOIN calls b ON b.number=a.number));
The join query on its own without the INSERT INTO clause works fine and returns a dataset. But the above query gives SQL syntax error

Change query syntax like this:
CREATE TABLE temp (`id` int, `number` int, `attempt` int);
INSERT INTO temp (`id`, `number`, `attempt`)
SELECT b.id, b.number, b.attempt FROM (
SELECT number FROM duplicate_numbers
) a
JOIN calls b ON b.number=a.number
Working demo: http://sqlfiddle.com/#!9/24f9f

Related

Mysql query and reordering results

My SQL query is like this
SELECT `product_code`, `test`
FROM `table_products`
WHERE `product_code` IN ('38986', '222098', '1113426', '3645651', ...)
I would like the results to be ordered as in product_code order shown in query and also when there is no matching for that product_code in table, I would like to add an empty line in SQL result.
There (probably) is no other way except that you express the values as rows:
SELECT codelist.code, table_products.whatever
FROM (
SELECT 38986 AS code, 1 AS sort UNION ALL
SELECT 222098, 2 UNION ALL
SELECT 1113426, 3 UNION ALL
SELECT 3645651, 4
) AS codelist
LEFT JOIN table_products ON codelist.code = table_products.product_code
ORDER BY codelist.sort
The LEFT JOIN will give you the code number and empty right hand side row if there is no match. ORDER BY sort will sort the products in the desired order.
You can have the reference product code values in another table and use right outer join
eg)
create table Test(id integer, title varchar(100));
create table ref(idtosee integer);//reference table
insert into ref(idtosee) values(1);
insert into ref(idtosee) values(4);
insert into Test(id, title) values(1, "Hello");
insert into Test(id, title) values(2, "sHello");
select id,title,idtosee from Test right outer join ref on id=idtosee;

Find values missing in a column from a set (mysql)

I am using mysql.
I have a table that has a column id.
Let us say I have an input set of ids. I want to know which all ids are missing in the table.
If the set is "ida", "idb", "idc" and the table only contains "idb", then the returned value should be "ida", "idc".
Is this possible with a single sql query? If not, what is the most efficient way to execute this.
Note that I am not allowed to use stored procedure.
MySQL will only return rows that exist. To return missing rows you must have two tables.
The first table can be temporary (session/connection specific) so that multiple instances can run simultaneously.
create temporary table tmpMustExist (text id);
insert into tmpMustExist select "ida";
insert into tmpMustExist select "idb";
-- etc
select a.id from tmpMustExist as a
left join table b on b.id=a.id
where b.id is null; -- returns results from a table that are missing from b table.
Is this possible with a single sql query?
Well, yes it is. Let me work my way to that, first with a union all to combine the select statements.
create temporary table tmpMustExist (text id);
insert into tmpMustExist select "ida" union all select "idb" union all select "etc...";
select a.id from tmpMustExist as a left join table as b on b.id=a.id where b.id is null;
Note that I use union all which is a bit faster than union because it skips over deduplication.
You can use create table...select. I do this frequently and really like it. (It is a great way to copy a table as well, but it will drop indexes.)
create temporary table tmpMustExist as select "ida" union all select "idb" union all select "etc...";
select a.id from tmpMustExist as a left join table as b on b.id=a.id where b.id is null;
And finally you can use what's called a "derived" table to bring the whole thing into a single, portable select statement.
select a.id from (select "ida" union all select "idb" union all select "etc...") as a left join table as b on b.id=a.id where b.id is null;
Note: the as keyword is optional, but clarifies what I'm doing with a and b. I'm simply creating short names to be used in the join and select field lists
There's a trick. You can either create a table with expected values or you can use union of multiple select for each value.
Then you need to find all the values that are in the etalon, but not in the tested table.
CREATE TABLE IF NOT EXISTS `single` (
`id` varchar(10) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `single` (`id`) VALUES
('idb');
SELECT a.id FROM (
SELECT 'ida' as id
UNION
SELECT 'idb' as id
UNION
SELECT 'idc' AS id
) a WHERE a.id NOT IN (SELECT id FROM single)
//you can pass each set string to query
//pro-grammatically you can put quoted string
//columns must be utf8 collation
select * from
(SELECT 'ida' as col
union
SELECT 'idb' as col
union
SELECT 'idc' as col ) as setresult where col not in (SELECT value FROM `tbl`)

Populate MySQL table with data from other MySQL tables

I would like to create a table that populates its data from other tables through SQL queries. For example:
Create TABLE1 with columns (AVERAGE_ROWS int, AVERAGE_RATING int)
Insert into TABLE1 values (SELECT COUNT(*) FROM TABLE X, SELECT AVG(rating) FROM TABLE X)
Insert into TABLE1 values (SELECT COUNT(*) FROM TABLE Y, SELECT AVG(rating) FROM TABLE Y)
Is this possible?
Thanks in advance, el retardo.
You will need to follow the format:
INSERT INTO table2 (column_name(s))
SELECT column_name(s) FROM table1;
So in your case it would look something like (SQL Fiddle):
INSERT INTO TABLE1 (AVERAGE_ROWS, AVERAGE_RATING)
SELECT COUNT(*), AVG(rating) FROM X
UNION
SELECT COUNT(*), AVG(rating) FROM Y;
As you can see in the example you are going to lose your presion on AVG(rating) since AVERAGE_RATING is declared as int. You would be better off using decimal(,) instead. Something like SQL Fiddle.

mysql insert multi row query result into table

I came across a scenario where I need to "upgrade" a table with data I obtain from another query. I am adding missing values so I will need to insert, but I cant seem to get it right.
The destination table is the following
CREATE TABLE `documentcounters` (
`UID` int,
`DataChar`,
`SeqNum` ,
`LastSignature`,
`DocumentType`,
`SalesTerminal`,
`Active`,
PRIMARY KEY (`UID`)
) ENGINE=InnoDB
and I am trying to do something like
INSERT INTO documentcounters
SELECT Q1.in_headers, -1,NULL, 17,0,0 FROM
(SELECT DISTINCT(DocumentSeries) as in_headers FROM transactionsheaders )AS Q1
LEFT JOIN
(SELECT DISTINCT(DataChar) as in_counters FROM documentcounters)AS Q2
ON Q1.in_headers=Q2.in_counters WHERE Q2.in_counters IS NULL;
I left UID out because I want the insert statement to create it, but I get a "Column count doesn't match" which makes sense (darn!)
Doing something like
INSERT INTO `documentcounters`
(`DataChar`,`SeqNum`,`LastSignature`,`DocumentType`,`SalesTerminal`,`Active`)
VALUES
(
(SELECT Q1.in_headers FROM
(SELECT DISTINCT(DocumentSeries) as in_headers FROM transactionsheaders )AS Q1
LEFT JOIN
(SELECT DISTINCT(DataChar) as in_counters FROM documentcounters)AS Q2
ON Q1.in_headers=Q2.in_counters WHERE Q2.in_counters IS NULL),-1,NULL,17,0,0
);
yields a "Subquery returns more than 1 row" error.
Any ideas how I can make this work?
Cheers
INSERT INTO `documentcounters`
(`DataChar`,`SeqNum`,`LastSignature`,`DocumentType`,`SalesTerminal`,`Active`)
SELECT Q1.in_headers, -1,NULL, 17,0,0 FROM
(SELECT DISTINCT(DocumentSeries) as in_headers FROM transactionsheaders )AS Q1
LEFT JOIN
(SELECT DISTINCT(DataChar) as in_counters FROM documentcounters)AS Q2
ON Q1.in_headers=Q2.in_counters WHERE Q2.in_counters IS NULL;
This will work if UID is defined as auto_increment.
If you want the INSERT to create the UID values, then UID must be defined as an auto-incrementing column.
CREATE TABLE `documentcounters` (
`UID` INT NOT NULL AUTO_INCREMENT,
...

How do I rewrite this MySQL query so it doesn't throw this error: You can't specify target table 'crawlLog' for update in FROM clause?

I'm trying to get an id from a companies table where the id is not yet in the crawlLog table. Then I need to insert that companyId into the crawlLog table.
I need to do this in one call so that parallel crawlers don't pull the same url after some other crawler has selected a url, but hasn't inserted it into the crawl log yet. I don't want to lock tables because of other problems that generates.
I get this error from both queries below:
You can't specify target table 'crawlLog' for update in FROM clause
Here are two queries i've tried to do the same thing.
INSERT INTO crawlLog (companyId, timeStartCrawling)
VALUES
(
(
SELECT companies.id FROM companies
LEFT OUTER JOIN crawlLog
ON companies.id = crawlLog.companyId
WHERE crawlLog.companyId IS NULL
LIMIT 1
),
now()
)
I've also tried this, but get the same error:
INSERT INTO crawlLog (companyId, timeStartCrawling)
VALUES
(
(
SELECT id
FROM companies
WHERE id NOT IN
(
SELECT companyId
FROM crawlLog
)
LIMIT 1
),
now()
)
Why use a Subselect? INSERT INTO ... SELECT exists:
INSERT INTO crawlLog (companyId, timeStartCrawling)
SELECT companies.id, NOW()
FROM companies
LEFT OUTER JOIN crawlLog
ON companies.id = crawlLog.companyId
WHERE crawlLog.companyId IS NULL
LIMIT 1
And that way it should not complain about using a table both in the INSERT and SELECT part
You can't update rows which you are querying. There is a way to force MySQL to use a temporary table implicitly:
INSERT INTO crawlLog (companyId, timeStartCrawling)
VALUES
(
SELECT id, when FROM
(
SELECT companies.id AS id, now() AS when FROM companies
LEFT OUTER JOIN crawlLog
ON companies.id = crawlLog.companyId
WHERE crawlLog.companyId IS NULL
LIMIT 1
)
)
This works and seems like the simplest solution:
Using the simpler of the two statements in my question, I created an alias for the inner crawlLog table as suggested by #Tocco in the comments, and then removed the necessary encapsulation in VALUES().
INSERT INTO crawlLog (companyId, timeStartCrawling)
SELECT id, now()
FROM companies
WHERE id NOT IN
(
SELECT companyId
FROM crawlLog AS crawlLogAlias
)
LIMIT 1
Do the select into a temp table, then insert selecting from the temp table. You can't insert into a table and select from it in the same statement, so use a temp table and two statements.