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).
Related
I have the following SQL statement running against a MariaDB 10.1.26 with ~2.000 rows with instant results.
select value, datetime from Schuppen
where (value = (select min(value) from Schuppen where (measure = 'temp')
and datetime between '2018-11-01 00:00:00' and '2018-11-02 00:00:00'))
and datetime between '2018-11-01 00:00:00' and '2018-11-02 00:00:00';
When I use the following statement with variables for the datetime fields, the execution takes ~5.5 seconds.
set #startdate = cast('2018-11-01 00:00:00' as datetime);
set #enddate = cast('2018-11-02 00:00:00' as datetime);
select value, datetime from Schuppen
where (value = (select min(value) from Schuppen where (measure = 'temp')
and datetime between #startdate and #enddate))
and datetime between #startdate and #enddate;
The more data rows I have, the longer it takes to execute the statement. Seems like the variables change the behaviour of the statement somehow.
What's wrong here?
I use MySQL Workbench and #variables are very useful to query/search different tables for a given attribute. I ran into a similar issue. After scouring through different threads and trying different things, it worked well when I set the #variable to be of exactly the same type and same encoding as the column in the table(s) that I am searching for that variable.
For example:
SET #keyword = CONVERT(CAST("KEYWORD" AS CHAR(8)) USING ASCII);
In this case, the search column cname in my table customer is of type CHAR(8) and encoded using ASCII:
SELECT * FROM CUSTOMERS WHERE cname=#keyword;
If you have multiple tables to query, where cname is CHAR(10) in one and CHAR(8) in another, then you can do the following:
SET #keyword = "KEYWORD";
SELECT * FROM CUSTOMERS WHERE cname=CONVERT(CAST(#keyword AS CHAR(8)) USING ASCII);
SELECT * FROM EMPLOYEES WHERE cname=CONVERT(CAST(#keyword AS CHAR(10)) USING ASCII);
The problem is that the query optimizer does a bad job on finding a suitable index when using variables. This is a known issue.
If you use EXPLAIN on both queries, you will see the difference. Just try to avoid variables when not necessary.
For the first query, the optimizer "sees" the chosen values and decides an index can be perfectly used to satisfy the selected range more efficiently.
For the second query, the optimizer is unaware of the two values that define the range, and decides to fall back to a FULL SCAN instead.
I am a newbie in coding.
Here is what I did:
DECALRE #v VARCHAR(100)
SET #v = (SELECT TOP 100 NAMES FROM TestTable WITH(NOLOCK))
SELECT #v AS SampleData
But it returned an error:
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Are there any way to set multiple values in one variable in SQL Server 2008?
Thanks in advance.
There is one type of variable designed for holding multiple values. It's called a table variable:
declare #v table (Name varchar(100) not null)
insert into #v(Name )
select top 100 name from TestTable /* no ORDER BY means this is ill-defined */
You can insert/update/delete in this table variable and query from it via selects in exactly the same way as any other table.
Note though that this looks like you're breaking things down into "procedural" steps - first I'll get the top 100 names, then I'll do X, then I'll do Y. In SQL, you should try to put as much as possible into single queries, and leave it to the optimizer to determine what order to do things in, which subresults should be stored, etc.
I'm trying to create a View as following:
CREATE VIEW v_MyView
AS
SET #par_count := 0; -- XXX Working in SELECT, but not in a View !? XXX
SELECT
q1.day,
q1.count_per_day,
(#par_count := #par_count + q1.count_per_day) AS count_sum -- sums up count_per_day
FROM
(SELECT
DATE(registration_date_time_obj) AS day,
COUNT(Date(registration_date_time_obj)) AS count_per_day
FROM tbl_registration_data
GROUP BY day
ORDER BY day
) AS q1
;
The select statement itself works fine, just creating a view fails in MySQL since it doesn't accept user variables/parameters within it's declaration i guess.
Is there a way to still create this view with a workaround for the parameter?
Anyways, i'm able to create a similar procedure for the select statement, but that doesn't really solve the problem since i can't call the procedure in another select statement...
Thanks for your suggestions and solutions! (:
MySQL documentation is pretty clear that variables are not allowed:
The SELECT statement cannot refer to system variables or user-defined variables.
Within a stored program, the SELECT statement cannot refer to program parameters or local variables.
You can do what you want using a correlated subquery:
SELECT DATE(registration_date_time_obj) AS day,
COUNT(Date(registration_date_time_obj)) AS count_per_day,
(SELECT COUNT(*)
FROM tbl_registration_data rd2
WHERE rd2.registration_date_time_obj <= date_add(date(rd.registration_date_time_obj), interval 1 day)
FROM tbl_registration_data rd
GROUP BY day
ORDER BY day;
Please read the documentation on VIEWS >> https://dev.mysql.com/doc/refman/5.5/en/create-view.html
A view definition is subject to the following restrictions:
The SELECT statement cannot contain a subquery in the FROM clause.
The SELECT statement cannot refer to system variables or user-defined variables.
Within a stored program, the SELECT statement cannot refer to program
parameters or local variables.
The SELECT statement cannot refer to prepared statement parameters.
Any table or view referred to in the definition must exist. After the
view has been created, it is possible to drop a table or view that
the definition refers to. In this case, use of the view results in an
error. To check a view definition for problems of this kind, use the
CHECK TABLE statement.
The definition cannot refer to a TEMPORARY table, and you cannot
create a TEMPORARY view.
You cannot associate a trigger with a view.
Aliases for column names in the SELECT statement are checked against
the maximum column length of 64 characters (not the maximum alias
length of 256 characters).
I am trying to use the date part of a time-stamp in my where query in a db2 pass-through proc SQL code below. I tried using date and datepart functions but it wont work with this format. Does anyone know the name of the function to use in the same code below?
PROC SQL;
connect to db2(ssid=smtng);
select * from connection to db2
(select *
from ATable
where DATEPART(timestamp) > '12/01/2013'
FOR READ ONLY WITH UR
);
DISCONNECT FROM DB2;
QUIT;
If you use a function on the datetime field in DB2 then the database won't be able to use it's indexes (if that field is indexed). This is because indexes are (almost always) created on the field itself, not the result of the field after it has been processed by a function. This holds true for the majority of databases not just DB2.
Instead, what you want to do is supply datetime values for the beginning of the day and for the end of the day and get everything inbetween them. To simplify this process I created a format called mysqldt.. Originally this format was for a mySQL database, but SQL server and DB2 both use the same formats so it can be used on those as well:
proc format;
picture mysqldt low-high = '''%Y-%0m-%0d %0H:%0M:%0S''' (datatype = datetime) ;
run ;
Once this format is available I tend to use macro variables. At the top of my program I would create a macro variable where I specify the date to use throughout the report:
%let rpt_date = %sysfunc(mdy(1,12,2013));
I would then create two datetime fields representing the start of the day and the end of the day, and I would save them in the format that is needed for the SQL statement:
%let sql_start = %sysfunc(dhms(&rpt_date, 0, 0, 0), mysqldt.);
%let sql_end = %sysfunc(dhms(&rpt_date,23,59,59), mysqldt.);
%put &rpt_date &sql_start &sql_end;
You would then change your query to look like this:
proc sql;
connect to db2(ssid=smtng);
select * from connection to db2
(select *
from atable
where timestamp between &sql_start and &sql_end
for read only with ur
);
quit;
This way, not only are your indexes now used in your query, but the SQL looks cleaner and reads easier, and you only need to change the report date in a single place (at the top of your program) if you need to rerun your report.
In general, you need to use the correct DB2 syntax. I don't know DB2, but this paper covers this fairly well. Specifically:
PROC SQL;
CREATE TABLE ONE AS SELECT * FROM CONNECTION TO DB2
(SELECT A.ID, A.NAME, B.AMOUNT, B.POSTDATE
FROM IDS A
INNER JOIN BANK B
ON A.ID = B.ID
WHERE POSTDATE BETWEEN '2007-01-01-00.00.00.000000'
AND '2007-09-30-23.59.59.999999')
So it looks like your query would be
PROC SQL;
connect to db2(ssid=smtng);
select * from connection to db2
(select *
from ATable
where DATEPART(timestamp) > '2013-12-01-00.00.00.00000'
FOR READ ONLY WITH UR
);
DISCONNECT FROM DB2;
QUIT;
This article from IBM seems to suggest that there are other formats other than timestamp (which is what above is). So you may need to use a different one depending on the exact format.
I believe, you should use SAS-date literal. So:
where DATEPART(timestamp) > '12Jan2013'd
I am getting this error while I am trying to execute a simple SELECT statement in Toad
MySql.Data.Types.MySqlConversionException
Unable to convert MySQL date/time value to System.DateTime
What could be wrong?
That could mean one of these two common issues:
1) Zero dates, which are 0000-00-00 in MySQL. MySQL allows you to store them to mark 0 dates, you can even use 0001-01-01, but not all drivers or downstream programs can handle them. Add to the connection string
Allow Zero Datetime=true;
The other choice is explicitly removing them, something like
SELECT IF(DateCol='0000-00-00' OR DateCol<'1970-01-01', NULL, DateCol) as DateCol,
Othercol1, ID ....
FROM TBL
2) Date formatting. For some driver/program combination, the dates are handled as strings. Explicit conversion is necessary:
SELECT DATE_FORMAT(DateCol, '%m/%d/%Y') as DateCol,
Othercol1, ID ....
FROM TBL