passing variable number of arguments to sql query - mysql

I want to pass variable number parameters to sql query. As an example:
select column1
from Table
where column2 in ( '0080','0010')
group by column1
having count(distinct column2) >= 2
In the where clause, 2 arguments 0080 and 0010 are used. But this number of arguments can vary based on the input from user. As an example it could be like this:
select column1
from Table
where column2 in ( '0080','0010', '0020', '0050', '0060')
group by column1
having count(distinct column2) >= 5
So, this number of arguments is not fixed and it will be passed by the user from an .xml file.
How can we pass a variable number of arguments to the query? As the number of arguments is not fixed and it can be changed from time to time, can we use an array or something similar?

I would suggest you try to load the arguments into a temporary table and use that in the where clause in a subquery. In particular if your argument list becomes very large, this is much more scalable.

Related

How to simplify a multiple cast MySql select statement?

I am trying to perform a MySql select with many conditions, but want to perform a string comparison on a column that is an integer datatype. Is there a way to do this without having to cast the column to a varchar on every single condition within the where clause?
SELECT COUNT(*)
FROM tablename
WHERE CAST(col1 AS VARCHAR(10)) NOT LIKE '558%'
AND CAST(col1 AS VARCHAR(10)) NOT LIKE '566%'
AND CAST(col1 AS VARCHAR(10)) NOT LIKE '567%'
AND CAST(col1 AS VARCHAR(10)) NOT LIKE '568%'
AND CAST(col1 AS VARCHAR(10)) NOT LIKE '569%'
AND CAST(col1 AS VARCHAR(10)) NOT LIKE '579%';
Before you ask why I'm not doing integer comparison: Instead of casting to a varchar, I could also just use plain integer comparison, but then I still would have to perform a math operation, i.e. col1/100000, for every item in the where clause, which leads to the same problem as to how can I simplify the statement?
You can use subquery:
SELECT COUNT(*)
FROM
(
SELECT CAST(col1 AS VARCHAR(10)) AS col1
FROM tablename
) AS t
WHERE t.col1 NOT LIKE '558%'
...
The direct answer to your question is that casts are implicit in MySQL, so col1 NOT LIKE '556%' is equivalent to what you're doing.
It's always best to avoid using functions that reference column names in the WHERE clause, because that disables the use of indexes and requires every row in the table to be evaluated. I assume that you are aware of that, since you mentioned you would still "have to do a math operation."
If you actually know the scale of the number then a more correct query would be...
WHERE (col1 < 556 * 100000 OR col1 > 556 * 100000)
AND ...
If that's logically correct based on what you are doing, then it's a better solution, because the optimizer will do that math only once, converting those into constants, rather than doing it once per row.
Also note that if you do know the scale of the numbers, then LIKE '556______' is also more logically valid than using % since _ matches exactly one character, where % matches zero or more.

Stored procedure to execute a query and return selected values if the query returns only 1 result

So my query is the following, which may return many results:
SELECT P_CODE, NAME FROM TEST.dbo.PEOPLE
WHERE NAME LIKE '%JA%'
AND P_CODE LIKE '%003%'
AND DOB LIKE '%1958%'
AND HKID = ''
AND (MOBILE LIKE '%28%' OR TEL LIKE '%28%')
I would like to integrate this into a Stored Procedure (or View?) so that it will only return a result if the query results in exactly 1 row. If there's 0 or > 1, then it should return no results.
If you just want to return an empty resultset in cases other than 1:
;WITH x AS
(
SELECT P_CODE, NAME, c = COUNT(*) OVER()
FROM TEST.dbo.PEOPLE
WHERE NAME LIKE '%JA%'
AND P_CODE LIKE '%003%'
AND DOB LIKE '%1958%'
AND HKID = ''
AND (MOBILE LIKE '%28%' OR TEL LIKE '%28%')
)
SELECT P_CODE, NAME FROM x WHERE c = 1;
Otherwise, you'll have to run the query twice (or dump the results to intermediate storage, such as a #temp table) - once to get the count, and once to decide based on the count whether to run the SELECT or not.
Effectively you want something akin to FirstOrDefault() from the Linq-to-SQL implementation but done on the server-side which means you will need to execute the query in a stored procedure, dumping the results into a temp table variable and then access ##ROWCOUNT afterwards to get the number of rows that were returned and then decide whether or not to forward the results on to the caller. If you do, be sure to use TOP 1 in the query from the temp table so that you only get a single result out as you desire.
UPDATE:
I described the alternate solution from what Aaron describes in his answer (which I like better).
Removed unnecessary TOP specifier in solution specification.

Use comma separated list from one table as clause in query for another table

I have an events table with a field called breaks. This is populated with data in a comma separated format, i.e. 1,2,3 or 1 or 1,4,5 - the same format that MySQL's IN command uses.
I'd then like to run a query - on the slots table - to return all rows apart from those specified in events.breaks.
The query, theoretically, should be something like this:
SELECT
`slots`.`id` AS id,
RIGHT(`slots`.`time`, 8) AS `time`
FROM
`slots`, `event`
WHERE
`slots`.`id` NOT IN (`event`.`breaks`)
But that doesn't appear to work - if event.breaks is 4,5,7, the only row from the slots table that doesn't return is 4!
SQLFiddle here: http://sqlfiddle.com/#!2/913fe/1/0
You're passing a single field to the NOT IN () clause, not a subexpression. Think of it like this
(1, 2, 3)
is roughly the same as
SELECT 1
UNION
SELECT 2
UNION
SELECT 3;
as a subexpression. What you're doing instead is
('4,5,7')
which is roughly equivalent to
SELECT '4,5,7';
which in turn MySQL probably converted to a number for the comparison and the result is
NOT IN (4)
What you're actually trying to do isn't really supposed to be done like that. It'd be better if you added an AxB relation table so you can select several rows with the IDs you don't want.
Give this a try:
SELECT slots.id AS id, RIGHT(slots.time, 8) time
FROM slots, event
WHERE FIND_IN_SET(slots.id, event.breaks) = 0
This is how the FIND_IN_SET(str,strlist) function works:
Returns a value in the range of 1 to N if the string str is in the string list strlist consisting of N substrings. A string list is a string composed of substrings separated by “,” characters. [...] Returns 0 if str is not in strlist or if strlist is the empty string.
Also note that IN (val1, val2, val3) is NOT the same as IN (val4) where val4 is a commma-separated string. The IN clause will compare by equality.
you may need a subselect to return the split string
... NOT IN (SELECT your_split_fnc(`event`.`breaks`) FROM `events`)
See answers here for a way to split strings in MySQL Can Mysql Split a column?
instr() MySQL function could be of help also
... INSTR(event.breaks,id) = 0
http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_instr

Issue writing MySQL query to find difference

I'm having trouble with sorting out a query for the following:
Data:
Column1 Column2
2 0
0 -2
I'm trying to select the difference between Column1 and Column2, with a minimum of 0. Eg.
Row1=2
Row2=0
My current query is SELECT (Column1 - Column2) as total FROM blah.
I tried adding max(Column2, 0) into the query, but that just errors.
Try:
SELECT GREATEST(Column1 - Column2, 0)
from Table
The MySQL function MAX() is an aggregate function, often used in combination with a GROUP BY. It only takes one argument (the name of the column of which you want to select the maximum value). The GREATEST() function is the function you need (as Michael Pakhantsov pointed out).

MySQL select with subquery having replace

So I have a data with format like ;1;;2; and then I need to use this number in a query so I thought I'd convert it to 1,2 and use that in a IN condition. In my table, the result should return 2 rows but instead it is returning only 1 row.
My query is like this. The subquery return 1,2 with no problem but only 1 row is retrieve.
select *
from wt_lists
where id IN ((select replace (replace(sendto, ';;',','),';','')
from wt_stats where statsid IN (1)))
But when I try it with this. It returns the correct result, which in my case is 2 rows.
select *
from wt_lists
where id IN (1,2)
What am I missing here?
Comma delimited strings need to be explicitly defined in the query in order to be used in the IN clause - there's countless examples on SO where people need to use dynamic SQL to incorporate user submitted comma delimited strings.
That said, I have a solution using the FIND_IN_SET function:
SELECT DISTINCT wl.*
FROM WT_LISTS wl
JOIN (SELECT REPLACE(REPLACE(ws.sendto, ';;',','),';','') AS ids
FROM WT_STATS ws
WHERE ws.statsid = 1) x ON FIND_IN_SET(wl.id, x.ids) > 0
You are replacing the string:
';1;;2;'
To:
'1,2'
So, you SQL query looks like:
select * from wt_lists where id IN ('1,2') from wt_stats where statsid IN (1)
To use IN clause you need select different values in different rows.
I found this store procedure that does exactly what you need.
http://kedar.nitty-witty.com/blog/mysql-stored-procedure-split-delimited-string-into-rows/
I have not tested, but it is the way.
Obs: Like David said in the comments above, parsing the data in your application is a better way to do this.