Remove "\N"s when doing SELECT INTO OUTFILE - mysql

I'm doing a SELECT INTO OUTFILE and it's showing a "\N" for every NULL value. Is there any way for me to make it just be blank instead?
I'm on MySQL.

You can use the COALESCE function something like this:
COALESCE(yourfield, '')

Good question, and as relevant today as it was in 2011. I use a stored procedure for this, wich takes the name of a table as argument. Subsequently, it converts all null-values to empty strings (''), using a prepaired statement and no cursors, for cursors are so non-SQL. I've tested it with a table with some 200 columns and a resulting prepaired statement that's about 15,000 characters long:
CREATE DEFINER=`root`#`localhost` PROCEDURE `ExportFixNull`(
in tblname tinytext)
BEGIN
set #string=concat(
"Update ",#tblname," set ",
(
select group_concat(column_name,"=ifnull(",column_name,",'')")
from information_schema.columns
where table_name=#tblname
)
,";"
);
prepare s1 from #string;
execute s1;
drop prepare s1;
In the main SQL-file, there is a statement
SET ##group_concat_max_len = 60000;
that might be crucial.
More details (in Dutch): http://wiki.devliegendebrigade.nl/SELECT_INTO_OUTFILE_%28MySQL%29
Regards,
Jeroen Strompf

Related

How do I pull out specific strings from a comma separated string?

I have a string like so;
"field1=lance,field2=peter,field3=john"
The actual string has 20+fields, and I want to pull out specific values by name.
For instance, I want to pull out the value for "field2" and return the value "peter",
Can someone give me an elegant way of doing this in MySQL?
I should mention that this is a standard field format coming out of an eCommerce system. I have no control over the format. It would be possible to extract the data cleanly through the API, but that would be significant extra work, especially as I have the data already in this format.
interesting question. There is a lot below so let's break it down. We essentially build a query and execute the stmt
DELIMITER $$
DROP PROCEDURE IF EXISTS proc_loop_test$$
CREATE PROCEDURE proc_loop_test()
#create empty query string
set #sqlstring = '';
#set your string of fields, not sure where this comes from
set #mystring = 'field1=lance,field2=peter,field3=john';
#create number of times we will loop through the string
set #num = (select length(#mystring)
- length(replace('field1=lance,field2=peter,field3=john',',','')) +1);
#init loop
loop LOOP
#create a short string just taking the "last" field/value pair
set #shtstring = (select SUBSTRING_INDEX(#mystring,',',-1));
#recreate your query string removing the substring we created
set #mystring = (select left(replace(#mystring,#shtstring,''),length(replace(#mystring,#shtstring,''))-1));
#add to your query string, we will build this for each
set #sqlstring = concat(#sqlstring ,(select
concat('''',SUBSTRING_INDEX(#shtstring,'=',-1),''''
,' as ',
left(#shtstring,(position('=' in #shtstring) -1))) ),',');
#reduce our count by one as we have removed the latest field
set #num = #num - 1;
#leave the loop when no fields left
if #num = 0 then leave LOOP;
end if;
end loop LOOP;
END$$
DELIMITER ;
#create a query statement to execute
set #query = (select concat('select ',left(#sqlstring, length(#sqlstring)-1)));
#execute the query!
PREPARE stmt FROM #query;
EXECUTE stmt;
Result
field3 field2 field1
john peter lance
There is no array logic, this would be simple in presto SQL etc. Because you have an arbitrary number of fields being defined at any time we are going to need to loop, and unfortunately you cannot loop in mysql without creating a procedure
That is the first few lines. We also create our full string from your source and the number of iterations (number of fields in string).
Then basically we isolate the "last" field/value pair iterively, rearrange each one so field1=john turns into more sql friendly 'john' as field',
We reduce our counter and string each time we loop through until counter is 0. At which point we stop.
We then prepare a query with our value/field pairs and a 'select' string. Then execute and you get your values as fields
Credit
Dynamic Strings prepare/exec
Looping and stored procs
Simulating Split function
This answer from #Akina's comment works perfectly.
SUBSTRING_INDEX(SUBSTRING_INDEX(column, 'field2=', -1), ',', 1)
And WHERE accordingly.

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;

Can't execute a MySQL SELECT statement when inside CONCAT

I have been trying to create a simple loop of SELECT statements in MySQL to reduce code. I have started this using CONCAT() however this causes the procedure to stop/fail. For example (where k is a loop counter):
CONCAT('SELECT (Child_', k, ' INTO #Age_Child_', k, ' FROM lookup_childage WHERE ModYear = ModYear_var LIMIT 1)');
To diagnose the issue, I simply tried to place the SELECT statement (without concatenated loop variables) inside a string to then be executed. While I could get this to work for simple statements it would not work for the following:
SET #queryString = CONCAT('SELECT Child_1 INTO #Age_Child_1 FROM lookup_childage WHERE ModYear = ModYear_var LIMIT 1');
PREPARE stmt FROM #queryString;
EXECUTE stmt;
Does anyone know why the #queryString containing the CONCAT() statement will not be executed/cause the procedure to fail?
tl;dr The statement you're trying to write has the form SELECT(rest of statement) LIMIT 1. It should have the form SELECT rest of statement LIMIT 1.
It looks like you want to create variable column names, ummm, because your lookup_childage table is denormalized. I guess that table has these columns.
Child_1 INT
Child_2 INT
Child_3 INT
Child_4 INT
It looks like you hope to get a #queryString value containing this sort of thing:
SELECT Child_4 INTO #Age_Child_4 FROM lookup_childage WHERE ModYear = ModYear_var LIMIT 1
Only the 4s are variable.
So to get that string you want
SELECT CONCAT('SELECT Child_', k,
' INTO #Age_Child_', k,
' FROM lookup_childage WHERE ModYear = ModYear_var LIMIT 1'
)
INTO #queryString;

MySql quick user select

I have a Users table and I'm getting user details (usual way) with id.
This is how I'm getting user details;
select id,kullaniciadi,FotoGet(K.Foto,K.Gender) from kullanicilar K where id=1;
FotoGet function always returning string value like 'Photos/ssss.jpg'
So far I have not trouble to use this way but I'm wondering how can I do this with a single function?
Like
set #uid=1;
Select UserGet(#uid);
I will put the id inside parentheses and the function will run my select query and return all user detail columns. id, kullaniciadi, FotoGet(K.id,K.Foto,K.Gender)
Can I use this way to get details?
A MySQL stored function cannot return multiple columns, it can only return a single scalar value.
But you could design a stored procedure that returns a result set:
CREATE PROCEDURE UserGet(uid INT)
SELECT id, kullaniciadi, FotoGet(K.Foto,K.Gender) FROM kullanicilar K WHERE id=uid;
Then call it like so:
CALL UserGet(1234);
It returns a result set just like a SELECT query would.
DELIMITER //
CREATE PROCEDURE UserGet(IN uid INT, IN column_name VARCHAR(64))
BEGIN
SET #sql = CONCAT('SELECT id, ', column_name, ' FROM kullanicilar K WHERE id=?');
PREPARE stmt FROM #sql;
SET #uid = uid;
EXECUTE stmt USING #uid;
END//
DELIMITER ;
Call it like so:
CALL UserGet(1234, 'kullaniciadi');
Remember that it's your responsibility to write code to pass a valid column name as the procedure argument. If you allow untrustworthy content to be passed, then it might be an SQL injection vulnerability.
Re your additional comment:
This should work fine:
CALL UserGet(1234, 'FotoGet(Foto,Gender)');

Using result of SQL Query as table name in mysql trigger

I've to write a trigger on my table which will perform the following functions.
Before Update on row, check price of Item
If price has changed from the last price, then select the table name, where to insert the item name, from another table having type of item and the associated table name.
Insert the item name in the selected table.
To put simply i've a table(TypeNameTable) having item categories and corresponding table names, if the price of item has changed then i've to get the table name from the TypeNameTable and insert the item name in the table, which is retrieved from TypeNameTable.
I'm not able to insert into table when I get the table names dynamically. Please suggest how to do it. Here's what I'm doing:
BEGIN
#declare countryTableName varchar(50);
declare itemPrice int;
declare itemTableName text;
IF (New.Price != Old.Price) THEN
SET countryTableName = (select `ItemManager`.`TypeNames`.`TypeTableName`
from `ItemManager`.`TypeNames`
where `ItemManager`.`TypeNames`.`ItemType` = NEW.ItemType);
INSERT INTO `ItemManager`.itemTableName
( `ItemName`, `ItemPrice`,
VALUES
( NEW.Name, New.Price );
END IF;
END$$
I get the error
ItemManager.itemTableName doesn't exists.
Answering my own question.
Figured out that using Dynamic SQL is not allowed in MySQL triggers . The restrictions are listed here.
However it's possible in Oracle where we can use PRAGMA AUTONOMOUS_TRANSACTION which executes the query in new context, and hence supports Dynamic SQL.
Example listed here at Point 27 .
You could CONCAT() your INSERT statement into a variable and execute that as PREPARED STATEMENT, someting like
...
SET #sql := CONCAT( 'INSERT INTO ', itemTableName, ' ... ' );
PREPARE stmt1 FROM #sql;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
...
afaik this is the only way to process dynamically generated SQL in stored routines and triggers.
If it is possible, I'd suggest you to change design a little. Instead of different tables you can create one table itemTable.
...
IF (New.Price != Old.Price) THEN
INSERT INTO `ItemManager`.`itemTable`
( `ItemName`, `ItemPrice`,
VALUES
( NEW.Name, New.Price );
END IF;
...
If there are different item properties, this table can be a parent table for specific child tables.