mySQL select which query evaluate by parameter - mysql

I've a query like this one:
SELECT IF(#param = 42, 'static', SELECT ... );
But it doesn't work because I can't insert a SELECT statement inside a IF(). My problem is that I can't do otherwise (use an if-then statement outside sql) because to "architecture restrictions".
Any solution to select if evaluate or not a query based to parameter value?

You don't need a select:
select (case when #param = 42 then 'static' else date_format(now(), '%Y-%m-%d') end)
Note that you are trying to mix two different types -- a datetime and string. You should explicitly convert the datetime to a string, using your preferred format.
You can write this with if(). case is slightly more general and the ANSI standard.

Related

Can't Set User-defined Variable From MySQL to Excel With ODBC

I have a query that has a user-defined variable set on top of the main query. Its something like this.
set #from_date = '2019-10-01', #end_date = '2019-12-31';
SELECT * FROM myTable
WHERE create_time between #from_date AND #end_date;
It works just fine when I executed it in MySQL Workbench, but when I put it to Excel via MySQL ODBC it always shows an error like this.
I need that user-defined variable to works in Excel. What am I supposed to change in my query?
The ODBC connector is most likely communicating with MySQL via statements or prepared statements, one statement at a time, and user variables are not supported. A prepared statement would be one way you could bind your date literals. Another option here, given your example, would be to just inline the date literals:
SELECT *
FROM myTable
WHERE create_time >= '2019-10-01' AND create_time < '2020-01-01';
Side note: I expressed the check on the create_time, which seems to want to include the final quarter of 2019, using an inequality. The reason for this is that if create_time be a timestamp/datetime, then using BETWEEN with 31-December on the RHS would only include that day at midnight, at no time after it.
Use subquery for variables values init:
SELECT *
FROM myTable,
( SELECT #from_date := '2019-10-01', #end_date := '2019-12-31' ) init_vars
WHERE create_time between #from_date AND #end_date;
Pay attention:
SELECT is used, not SET;
Assinging operator := is used, = operator will be treated as comparing one in this case giving wrong result;
Alias (init_vars) may be any, but it is compulsory.
Variable is initialized once but may be used a lot of times.
If your query is complex (nested) the variables must be initialized in the most inner subquery. Or in the first CTE if DBMS version knows about CTEs. If the query is extremely complex, and you cannot determine what subquery is "the most inner", then look for execution plan for what table is scanned firstly, and use its subquery for variables init (but remember that execution plan may be altered in any moment).

MYSQL - How to cast a field in depending on its value?

I have complex SQL-Statements with group_concat and i need sometimes to convert the value. So i use something like this (Its an example):
SELECT IF(1=1,CAST(TEST AS CHAR),CAST(TEST AS UNSIGNED))
FROM(
SELECT '40' as TEST
UNION
SELECT '5' as TEST
UNION
SELECT '60' as TEST
) as t1
ORDER BY TEST ASC;
Should return CHAR, returns CHAR.
SELECT IF(1=0,CAST(TEST AS CHAR),CAST(TEST AS UNSIGNED))
FROM(
SELECT '40' as TEST
UNION
SELECT '5' as TEST
UNION
SELECT '60' as TEST
) as t1
ORDER BY TEST ASC;
Should return UNSIGNED, returns CHAR
So the result of IF condition is always a CHAR and need to be CAST.
How i can resolve the problem?
MySQL tries to figure out the column type just by parsing the SQL, not based on runtime conditions like the way an IF() expression goes. While your example allows the result of the IF() to be determined before actually processing any rows, this is not true in general and MySQL doesn't perform this optimization.
It just sees that the IF() can return either UNSIGNED or CHAR, and figures out a common type that both can be converted to safely. This is CHAR.
This is explained in the documentation:
The default return type of IF() (which may matter when it is stored into a temporary table) is calculated as follows:
If expr2 or expr3 produce a string, the result is a string.
If expr2 and expr3 are both strings, the result is case-sensitive if either string is case sensitive.
If expr2 or expr3 produce a floating-point value, the result is a floating-point value.
If expr2 or expr3 produce an integer, the result is an integer.

Parameter as a result

I am looking for a way to use a parameter as a result that can be plugged in to another select statement later down the line.
This is the CTE select statement that I am able to pull by manually changing all four of the dates listed to what I want
SELECT CurrentYearEmp.PRCo,
CurrentYearEmp.Employee,
CASE
WHEN CurrentYearEmp.PREndDate <> '2016-11-20'
THEN '2016-11-20 00:00:00'
WHEN CurrentYearEmp.PREndDate = '2016-11-20'
THEN '2016-11-20 00:00:00'
END AS 'ParameterPREndDate'
I am able to replace the first half of the WHEN statement with a parameter like this
SELECT CurrentYearEmp.PRCo,
CurrentYearEmp.Employee,
CASE
WHEN CurrentYearEmp.PREndDate <> #PREndDate
THEN '2016-11-20 00:00:00'
WHEN CurrentYearEmp.PREndDate = #PREndDate
THEN '2016-11-20 00:00:00'
END AS 'ParameterPREndDate'
But it will only produce the results I am looking for if the parameter is 2016-11-20. I want to be able to have #PREndDate as '2017-02-14' it will post 2017-02-14 as a result of the select statement.
You can use a Table Valued Function as HABO has already said. Here is a simple example for one:
/*
--Create some dummy data in a db
CREATE TABLE SomeData
(
DataId INT IDENTITY
, Val VARCHAR(8)
, Dt DATE
)
INSERT INTO dbo.SomeData (Val, Dt) VALUES ('A', '2017-1-1'),('B', '2017-1-2'),('C', '2017-1-3'),('D', '2017-1-4'),('E', '2017-1-5')
--Create a table valued function
CREATE FUNCTION ReturnData (#StartDt DATE, #EndDt Date)
RETURNS TABLE
AS
Return
Select *
From SomeData
WHERE DT between #StartDt and #EndDt
*/
Select *
From ReturnData('1-1-2017', '1-3-2017')
The best thing IMHO about Table Functions is they can join to existing things as they are well formed objects in the database. When you do Procedures and Dynamic SQL, you get a result set but that does not mean your code can be joined. Table Functions can be cross applied to run off of cte's and they can be joined to other tables. So essentially they are better for reuse but they have rules around them so you cannot do more advanced things like dynamic sql in them(as far as I know unless they changed it recently) and other things. But if you want a reusable data set with rules governing input, that is pretty much exactly what they are made for.

Does MySQL "lazy evaluate" when having queries inside IF (conditional) statements? [duplicate]

I need to query data from a second table, but only if a rare set of conditions in the primary table is met:
SELECT ..., IF(a AND b AND c AND (SELECT 1 FROM tableb ...)) FROM tablea ...
a, b, and c conditions are almost always false, so my thinking is the subquery will never execute for most rows in the result set and thus be way faster than a join. But that would only true if the IF() statement short circuits.
Does it?
Thanks for any help you guys can provide.
The answer is YES.
The IF(cond,expr_true,expr_false) within a mysql query is short-circuited.
Here a test, using #variables to prove the fact:
SET #var:=5;
SELECT IF(1 = 0, (#var:=#var + 1), #var ); -- using ':=' operator to modify 'true' expr #var
SELECT IF(1 = 1, #var, (#var:=#var + 1) ); -- using ':=' operator to modify 'false' expr #var
SELECT #var;
The result is '5' from all three SELECT queries.
Had the IF() function NOT short circuited, the result would be a '5' from SELECT #1, and '6' from SELECT #2, and a '7' from the last "select #var".
This is because the 'true' expression is NEVER executed, in select #1 and nor is the false expression executed for select #2.
Note the ':=' operator is used to modify an #var, within an SQL query (select,from, and where clauses). You can get some really fancy/complex SQL from this. I've used #vars to apply 'procedural' logic within a SQL query.
-- J Jorgenson --
With J. Jorgenson's help I came up with my own test case. His example does not try to short circuit in the condition evaluation, but using his idea I came up with my own test and verified that MySQL does indeed short-circuit the IF() condition check.
SET #var:=5;
SELECT IF(1 = 0 AND (#var:=10), 123, #var); #Expected output: 5
SELECT IF(1 = 1 AND (#var:=10), #var, 123); #Expected output: 10
On the second example, MySQL is properly short-circuiting: #var never gets set to 10.
Thanks for the help J. Jorgenson!
It depends.
IF doesn't short-circuit such that it can be used to avoid truncation warnings with GROUP_CONCAT, for example in:
set ##group_concat_max_len = 5;
select if(true or #var:=group_concat('warns if evaluated'), 'actual result', #var);
the result will be 'actual result' but you'll get a warning:
Warning (Code 1260): Row 1 was cut by GROUP_CONCAT()
which is the same warning you get with less trivial GROUP_CONCAT expressions, such as distinct keys, and without the IF at all.
Try it in the SQL analyzer. If you want to be on the safe side and not have to trust the database to work one way (and not to change that behavior ever in new versions), just make two queries and do the IF programmatically.

Linq-to-SQL expression to get the max numeric value from a text column

I have an nvarchar SQL column which contains mostly numeric values. I'm trying to come up with an L2S expression that gets me the maximum numeric value while ignoring any non-numeric values.
The SQL that does the job is:
select top 1 value from identifier where and patindex('%[^0-9]%', value) = 0 order by CAST(value AS INT) desc
What LINQ expression can I use to achieve the same thing?
You can do SQL Like queries using the SqlMethods in System.Data.Linq.SqlClient.
(from a in identifier
where !SqlMethods.Like(a.value, "%[^0-9]%")
select a).Max(x => Convert.ToInt64(x.value))
This Linq statement becomes this query according to LinqPad:
-- Region Parameters
DECLARE #p0 VarChar(8) = '%[^0-9]%'
-- EndRegion
SELECT MAX([t1].[value]) AS [value]
FROM (
SELECT CONVERT(BigInt,[t0].[value]) AS [value], [t0].[value]
FROM [Identifier] AS [t0]
) AS [t1]
WHERE NOT ([t1].[value] LIKE #p0)
LinqPad is a great way to play around with queries to see if you can get what you're looking for. I've found that just about the only SQL statements that don't have a good L2S equivalent are ones with the PIVOT keyword. Other than that, there's usually a way to get what you want.
If you want the whole record and not just the MAX() value, you could do the query this way:
(from a in Accounts
orderby (!SqlMethods.Like(a.AccountNumber, "%[^0-9]%") ?
Convert.ToInt64(a.AccountNumber) : 0)
descending
select a).Take(1)