JSON_EXTRACT as VALUES for INSERT INTO? - mysql

I expect the following MySQL code:
SET #json= '{"1":{"name":"Name","value":"James Bowery","id":1,"type":"name","first":"James","middle":"","last":"Bowery"},"2":{"name":"Birthdate","value":"06\/23\/2017","id":2,"type":"date-time","date":"06\/23\/2017","time":"","unix":1498176000},"3":{"name":"Gender","value":"Male","value_raw":"Male","id":3,"type":"radio"},"4":{"name":"Ethnicity","value":"European","value_raw":"European","id":4,"type":"radio"},"5":{"name":"Email","value":"jabowery#emailservice.com","id":5,"type":"text"}}';
SET #array = JSON_EXTRACT( #json, '$."1".first','$."1".last','$."5".value','$."2".value','$."3".value','$."4".value');
INSERT INTO user (firstname,lastname,birthdate,gender,ethnicity,email) VALUES (#array);
To insert a row into the user table populated by the named fields extracted from the JSON. However, the INSERT yields a syntax error. What is the proper syntax?

Perhaps try this. Note that I have changed the date format to yyyy-MM-dd. You will need to work on converting this programatically to fit to MySQL. Also, when I ran this, the array had email before birthdate, so I changed the sequence of insert.
SET #json= '{"1":{"name":"Name","value":"James Bowery","id":1,"type":"name","first":"James","middle":"","last":"Bowery"},"2":{"name":"Birthdate","value":"06\/23\/2017","id":2,"type":"date-time","date":"2017-06-23","time":"","unix":1498176000},"3":{"name":"Gender","value":"Male","value_raw":"Male","id":3,"type":"radio"},"4":{"name":"Ethnicity","value":"European","value_raw":"European","id":4,"type":"radio"},"5":{"name":"Email","value":"jabowery#emailservice.com","id":5,"type":"text"}}';
SET #array = JSON_EXTRACT( #json, '$."1".first','$."1".last','$."5".value','$."2".value','$."3".value','$."4".value');
SET #queryString= CONCAT( 'INSERT INTO user (firstname,lastname,email,birthdate,gender,ethnicity) VALUES (',REPLACE(REPLACE(#array, '[',''),']',''),')');
PREPARE stmt FROM #queryString;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

Related

Composing a dynamic query in a stored procedure in MYSQL

I am creating a stored procedure in MySQL in order to execute the same queries in several parts of my application.
One of the two tables, as you can guess from the code, will be different depending on the script that will execute the stored procedure. The fields of the second query will be the same in number and type. The name of the table and the name of a column will change.
In order to make a dynamic query, I have used the CONCAT() command but I don't like it very much because it contains too many fragmented parts and too many quotes. Is there a more elegant way to compose a dynamic query like this?
BEGIN
INSERT IGNORE INTO tag (cod, tag) values (cod_tag_in, tag_in);
SET #query = CONCAT("INSERT INTO ", table_in, " (cod, ", table_field_in, ", tag_cod) values ('", app_cod_in, "','", section_cod_in, "', (SELECT cod FROM tag WHERE tag = '", tag_in, "'))");
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
Here is how I would write this routine if the table name and field name are really dynamic:
BEGIN
INSERT IGNORE INTO tag (cod, tag) values (cod_tag_in, tag_in);
SET #query = CONCAT('INSERT INTO `', table_in, '` (cod, `', table_field_in, '`, tag_cod) values (?, ?, ?)');
SET #cod = app_cod_in, #field = section_code_in, #tag_cod = cod_tag_in;
PREPARE stmt FROM #query;
EXECUTE stmt USING #cod, #field, #tag_cod;
DEALLOCATE PREPARE stmt;
END
Use query parameters so you don't have to use all those meticulous quotes for the values. This also protects you in case the values themselves contain quote characters.
I don't see the need for the subquery, since the value you just inserted in the the tag table should be the same as cod_tag_in anyway.
You do need the breaks in quotes for the dynamic table name and dynamic field name, because you can't use parameters as table or column identifiers.
I put back-ticks around the table and field name, just in case these identifiers require them (conflict with SQL reserved keywords, contain whitespace or punctuation, etc.).
However, you shouldn't even use dynamic SQL for this at all. You should use a CASE statement for the tables you need to support:
BEGIN
INSERT IGNORE INTO tag (cod, tag) values (cod_tag_in, tag_in);
CASE table_in
WHEN 'mytable1' THEN
INSERT INTO mytable1 (cod, myfield1, tag_cod) VALUES (app_cod_in, section_code_in, cod_tag_in);
WHEN 'mytable2' THEN
INSERT INTO mytable2 (cod, myfield2, tag_cod) VALUES (app_cod_in, section_code_in, cod_tag_in);
WHEN 'mytable3' THEN
INSERT INTO mytable3 (cod, myfield3, tag_cod) VALUES (app_cod_in, section_code_in, cod_tag_in);
ELSE
SIGNAL SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Unknown table';
END CASE;
END
This means you don't have to worry about CONCAT()-ing any fragments of SQL, and you don't have to worry about SQL injection risks, and you don't have to worry about table or column identifiers that have conflicting characters.
But you are limited to the tables you have coded in the CASE statement. If you are in the habit of creating new tables on the fly frequently, you'd have to replace the stored procedure with longer and longer CASE statements. But if you need to do that, I'd reconsider the design that so frequently requires new tables of similar structure.

MySQL - create a table with all of the fields that two tables have [duplicate]

I have to convert a MSSQL stored proc that passes a varchar that is a query:
INSERT INTO Results
EXEC (#Expresion);
This isn't working. I'm pretty sure that EXEC and EXECUTE aren't MySQL commands, but CALL doesn't work either.
Does anyone know if it's even possible to have something like JavaScript's eval function for MySQL?
I think you're looking for something like this:
SET #queryString = (
SELECT CONCAT('INSERT INTO user_group (`group_id`,`user_id`) VALUES ', www.vals) as res FROM (
SELECT GROUP_CONCAT(qwe.asd SEPARATOR ',') as vals FROM (
SELECT CONCAT('(59,', user_id, ')') as asd FROM access WHERE residency = 9
) as qwe
) as www
);
PREPARE stmt FROM #queryString;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET #asd = NULL;
This is the SQL equivalent of eval(my_string);:
#Expression = 'SELECT "Hello, World!";';
PREPARE myquery FROM #Expression;
EXECUTE myquery;
Basically I combined the existing answers, neither tells you how to do eval exactly.
If you want to add parameters, you can use this:
#username = "test";
#password = "asdf";
#Expression = 'SELECT id FROM Users WHERE name = ? AND pass = ?;'
PREPARE myquery FROM #Expression;
EXECUTE myquery USING #username, #password;
And to answer the original question exactly:
#Expression = 'SELECT "Hello, World!";'
PREPARE myquery FROM #Expression;
INSERT INTO Results
EXECUTE myquery;
Note that the PREPARE ... FROM statement wants a session variable (prefixed with #). If you try to pass a normal variable, it will throw its hands up in the air and it just won't care.
EXECUTE is a valid command in MySQL. MySQL reference manual
The EXECUTE MySQL command can only be used for one prepared statement.
If case you want to execute multiple queries from the string, consider saving them into file and source it, e.g.
SET #query = 'SELECT 1; SELECT 2; SELECT 3;';
SELECT #query INTO OUTFILE '/tmp/temp.sql';
SOURCE /tmp/temp.sql;

Mysql: user defined variable in Insert statement

I am trying to use following mysql code to insert on column whose name is decided on some value. It gives me error that "some_percent" unknown column 'some_percent' in 'field list'
set #SplitState = `some_percent`;
SET #ID = uuid();
INSERT INTO `rspca_donations`(`id`, `#SplitState`) values (#ID, '100');
Can someone please suggest correct way?
You can't use a user variable to represent a column name in this way. One option here would be to use a prepared statement in MySQL:
SET #SplitState = 'some_percent';
SET #ID = UUID();
SET #sql = CONCAT('INSERT INTO rspca_donations (id, ', #SplitState, ') VALUES (?, 100)');
PREPARE stmt FROM #sql;
EXECUTE stmt USING #ID;

How to set a local list/tuple variable in mysql

Is there a way to do the following in mysql?
SET #studios = ('Disney', 'Warner Bros.', 'Fox');
SELECT * FROM movies WHERE provider IN #studios;
When I try doing the above I get the error:
Operand should contain 1 column(s)
The error is coming from your initial assignment. You cannot assign lists to variables.
The only way of doing this in MySQL is to either create a temp table to hold the values, and then do ... IN (SELECT someVal FROM thatTemp), or to dynamically create the query with the values directly in the query string.
Example temp table creation:
CREATE TEMPORARY TABLE `someTemp` ( someVal VARCHAR(16) );
INSERT INTO `someTemp` (someVal) VALUES ('a'), ('b'), ('c');
SELECT * FROM myTable WHERE myField IN (SELECT someVal FROM someTemp);
DELETE TEMPORARY TABLE `someTemp`;
Alternatively, there is also FIND_IN_SET, which could be used like this:
SET #list = 'a,b,c';
SELECT * FROM myTable WHERE FIND_IN_SET(myField, #list) <> 0;
but this method probably has extremely poor performance (and may not be useable if your "myField" values may contain commas).
It is not possible to set a tuple/list/array in a user-defined variable in MySQL. You can use Dynamic SQL for the same:
-- we use single quotes two times to escape it
SET #studios = '(''Disney'', ''Warner Bros.'', ''Fox'')';
-- generate the query string
SET #query = CONCAT('SELECT * FROM movies WHERE provider IN ', #studios);
-- prepare the query
PREPARE stmt FROM #query;
-- execute it
EXECUTE stmt;
-- deallocate it
DEALLOCATE PREPARE stmt;
You could concatenate your list to a string, and use FIND_IN_SET as your criteria. Might not be super efficient, but makes the code quite easy to read and maintain.
Looks like this:
SET #studios = CONCAT_WS(',',
'Disney',
'Warner Bros.',
'Fox'
);
SELECT * FROM movies
WHERE FIND_IN_SET(provider, #studios) <> 0;

EDIT mysql tablename to combine with value of one column (concatenate)

I have a table called 'Details', the Details table has a few columns, one being 'TicketNumber'.
What I am hoping to do is name the table 'Details_TicketNumber' --- not the actual word but the value of the first/highest ticket number.
e.g if the TicketNumber is '12345'
the table name would be Details_12345
How would I be able to do this? I've been searching for a few hours and no luck today.. Thanks
This is what I have tried (and realized it wouldn't work)
attempt 1:
Rename table details
to (select concat("details",details.ticketnumber));
Attempt 2:
set #sql = CONCAT(details,
details.TicketNumber)
);
prepare s from #sql;
execute s;
As suggested by #JeffUK and the reference he pointed out. I believe this should work inside a stored procedure.
DECLARE highest_ticket INT DEFAULT 0;
SELECT MAX(ticket_number) INTO highest_ticket
FROM details;
set #s = CONCAT('RENAME TABLE details to details_',highest_ticket);
prepare renameTable from #s;
EXECUTE renameTable ;
DEALLOCATE PREPARE renameTable ;