Can you use an mysql #variable in WHERE IN (SELECT #var)? - mysql

Can you use an mysql #variable in WHERE IN clause ?
I tried this:
SET #inlist =
(
SELECT GROUP_CONCAT(product_id) FROM products WHERE `status` = "new"
);
SELECT #inlist; -- shows comma separated values;
SELECT * from products where product_id IN (SELECT #inlist);
will only return the first product in the list.
I know there is a default 1k limit on GROUP_CONCAT.

You can do it with dynamic SQL:
PREPARE stmt FROM CONCAT('SELECT * from products where product_id IN (', #inlist, ')');
EXECUTE stmt;
If you need to do this frequently, you could put it in a stored procedure, and then just do:
CALL yourProc(#inlist);

try this with FIND_IN_SET
SELECT * from products where FIND_IN_SET(product_id , #inlist );

Related

MySQL: How do you concat a column name with AS?

I am trying to get it so that I can use a variable in the column name but it doesn't seem to be working.
select
v.name as concat(#person,'\'s Vendor')
,sum(t.amount) as 'Total'
from
total t
inner join vendor v
on v.d = t.d
where
t.who = #person
and v.name is not null
and t.sub_cat not like 'Payment'
and year(date) = (year(sysdate())-1)
and amount > 0
group by
v.name
order by
sum(amount)
desc limit 20;
The above throws an error but below works.
select
v.name as 'John\'s Vendor'
,sum(t.amount) as 'Total'
from
total t
inner join vendor v
on v.d = t.d
where
t.who = #person
and v.name is not null
and t.sub_cat not like 'Payment'
and year(date) = (year(sysdate())-1)
and amount > 0
group by
v.name
order by
sum(amount)
desc limit 20;
Basically, I want to be able to add the person variable and some more text as the column name to better identify the purpose of the data, in this case, who the data is about. Also, I would like to use this as a stored procedure:
create procedure GetTotals(IN person varchar(10));
delimiter //
begin
above working code
end //
delimiter ;
You can use dynamic sql for such queires
SET #sql = CONCAT("select
v.name as '",#person,
"\\'s Vendor'
,sum(t.amount) as 'Total'
from
total t
inner join vendor v
on v.d = t.d
where
t.who = #person
and v.name is not null
and t.sub_cat not like 'Payment'
and year(date) = (year(sysdate())-1)
and amount > 0
group by
v.name
order by
sum(amount)
desc limit 20;");
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Keep in mind thsi can be used for sql injection, so a white list for the name is in order
Why not just add the parameter as its own column with a fixed column name. Your return process will be responsible for presenting and can just use that extra column as the "who's" it for purposes. Ex:
select
v.name,
sum(t.amount) as 'Total',
concat(#person,'\'s Vendor') as ForWho
from
Yes, every row will return a result of ex: "John's Vendor" in the column ForWho, but at least you wont have to format that in the return set, you have the column.
Identifiers, including aliases, must be fixed at the time the query is parsed. You can't make an alias that depends on an expression.
You could make a dynamic SQL query by formatting an SQL statement using your #person variable:
SET #sql = CONCAT(
'select v.name as `', concat(#person,'\'s Vendor'), '`,',
...
' where t.who = ? ',
...
);
PREPARE stmt FROM #sql;
EXECUTE stmt USING #person;
DEALLOCATE PREPARE stmt;
The value of #person can be set as the parameter where the ? placeholder appears in the prepared query syntax. But the usage in the column alias cannot be parameterized. So you just have to be careful that the value of #person is safe to use in that position.
You should delimit the alias with back-ticks like any other identifier, to protect from syntax errors if #person contains whitespace or punctuation characters.

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;

iterating over arrays in MYSQL

Is there a way to pass an array into a MySQL query and return the results as another array ?(apart from using cursors which would be an overkill for my use case)
For a single id, my query looks like this.
SET #userId = '04b452cd59dcc656'
Select user_account_number from userstore where u_id = #userId ;
Instead of sending each id at a time, I am trying to send a list and return a list
SET #userId = ('04b452cd59dcc656','eqwe52cddasfsd656');
<query returning the list of account numbers>
Also - I think this would be efficient over just sending one id at a time. Thoughts ?
You can use IN:
select user_account_number
from userstore
where u_id in ('04b452cd59dcc656', 'eqwe52cddasfsd656') ;
Using variables is trickier. If you know a maximum number, you can do:
select user_account_number
from userstore
where u_id in (#id1, #id2);
Not satisfying, but it does the job. Similarly unsatisfying is FIND_IN_SET():
set #ids = '04b452cd59dcc656,eqwe52cddasfsd656';
select user_account_number
from userstore
where find_in_set(u_id, #ids) > 0;
Alas, this won't use an index.
Finally there is dynamic SQL:
set #sql = concat('select user_account_number from userstore where u_id in (''',
replace(ids, ',', ''','''),
''')'
);
prepare s from #sql;
execute s;

Table name as field

Is it possible to query a table whose name comes from a sub-query?
For example.,
SELECT * FROM <TABLE_NAME IS <SUB_QUERY>>
select * from (
(select distinct(name) from category where id = 3 limit 1) CAT);
INNER QUERY RESULTS --> DEPARTMENT;
So it has to fetch from department table.
Using Mysql as DB.
You should use Prepared Statements.
In your case it should be:
select #name := name from (
(select distinct(name) from category where id = 3 limit 1) CAT);
set #sqlquery := 'select * from ' . #name ;
prepare qry from #sqlquery ;
execute qry;
deallocate prepare qry;
This might be helpful SQL Syntax for Prepared Statements
In two words: you can execute sql commands specified in varchar variables which can be produced by concatenation and other stuff.

MySQL: alias column name question

Is it possible to alias a column name with the result of a simple SELECT query.
This doesn't work:
SELECT `hlevel1` AS (SELECT `level1` FROM `hierarchy_labels` LIMIT 1) FROM `hierarchy`;
Any Suggestions?
You can't do this.
Aliases are used to rename a field or to name a calculated field.
If you simply want your results to be named 'hlevel1', you may want to try this:
SELECT level1 as hlevel1 FROM hierarchy_labels LIMIT 1
Use a prepared statement.
SELECT `level1` INTO #x FROM `hierarchy_labels` LIMIT 1;
SET #s = CONCAT('SELECT `hlevel1` AS `', #x, '` FROM `hierarchy`');
PREPARE s FROM #s;
EXECUTE s;
DEALLOCATE PREPARE s;