Possible to emulate a recursive CTE? - mysql

I think the short answer to this is No, but is it possible to emulate a recursive CTE with something like a table function in a database that doesn't have support for a CTE? For example:
-- get weekday names -- 'Monday', 'Tuesday', ...
WITH cte_numbers(n, weekday) AS (
SELECT
0,
DATENAME(DW, 0)
UNION ALL
SELECT
n + 1,
DATENAME(DW, n + 1)
FROM
cte_numbers
WHERE n < 6
)
SELECT
weekday FROM cte_numbers;
For example could the above pattern be rewritten to run in Mysql5.7, or recursion cannot be emulated there?

None of the solutions is very efficient, and most of them involve writing more code than you should. It would be better to upgrade to MySQL 8.0.
SQL works best on sets, not on iteration. So the standard way to generate a series in a single query is to already have a set of rows in a temporary table, and apply some set-based operations to it.
For example:
SELECT 0 AS num UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5;
+-----+
| num |
+-----+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
+-----+
With this as the basis, you can do date arithmetic and then extract the weekday names with DATE_FORMAT():
SELECT DATE_FORMAT(CURDATE() + INTERVAL num DAY, '%W') AS weekday
FROM (
SELECT 0 AS num UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5
) AS t;
+-----------+
| weekday |
+-----------+
| Friday |
| Saturday |
| Sunday |
| Monday |
| Tuesday |
| Wednesday |
+-----------+
You could also prepare a fixed base table of integers, fill it with as many as you need, and use it for different purposes.
SELECT DATE_FORMAT(CURDATE() + INTERVAL num DAY, '%W') AS weekday
FROM MySetOfIntegers
WHERE num BETWEEN 0 AND 5;
The suggestion of using an iterative approach would involve writing a lot more code. It will also mean N SQL queries, each generating a separate result set, so that's more code you have to write in your application to fetch all the result sets and append them together.
You could write a recursive stored procedure, but there's a risk of exceeding the thread stack space if you allow deep recursion. The default limit on stored procedure recursion is 0. That is, no recursion is allowed at all unless you set a finite limit. See https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_max_sp_recursion_depth
Here's an example of a recursive stored procedure:
DROP PROCEDURE IF EXISTS Weekdays;
DELIMITER //
CREATE PROCEDURE Weekdays(IN date DATE, IN num INT)
BEGIN
IF num >= 1 THEN
CALL Weekdays(date, num-1);
END IF;
SELECT DATE_FORMAT(date + INTERVAL num-1 DAY, '%W') AS weekday;
END//
DELIMITER ;
And calling it. Note it produces multiple result sets.
mysql> set max_sp_recursion_depth=6;
mysql> call Weekdays(CURDATE(), 6);
+----------+
| weekday |
+----------+
| Thursday |
+----------+
1 row in set (0.00 sec)
+---------+
| weekday |
+---------+
| Friday |
+---------+
1 row in set (0.00 sec)
+----------+
| weekday |
+----------+
| Saturday |
+----------+
1 row in set (0.00 sec)
+---------+
| weekday |
+---------+
| Sunday |
+---------+
1 row in set (0.00 sec)
+---------+
| weekday |
+---------+
| Monday |
+---------+
1 row in set (0.00 sec)
+---------+
| weekday |
+---------+
| Tuesday |
+---------+
1 row in set (0.00 sec)
+-----------+
| weekday |
+-----------+
| Wednesday |
+-----------+
1 row in set (0.00 sec)
I may have gotten the recursion off by one somewhere. This actually supports the point that it's not as easy as it sounds to implement a recursive routine.
Oh and you get an error — and no results — if you exceed the recursion limit.
mysql> call Weekdays(CURDATE(), 8);
ERROR 1456 (HY000): Recursive limit 6 (as set by the max_sp_recursion_depth variable) was exceeded for routine Weekdays

Related

Mysql select Month names as numeric

I need to get Month number from event table using a mysql select query. here my EVENT table,
query
SELECT month(str_to_date(month,'%d')) AS m FROMevent``
but the result was
how can I convert my month column as numeric..
Use %M insted of %d
mysql> SELECT month(str_to_date('April','%M')) AS m;
+------+
| m |
+------+
| 4 |
+------+
1 row in set (0.05 sec)

How to pass dynamic parameter to select statement in MySQL?

I have a query in MySQL:
select id, 1 from table;
When I run this query I will get the result as below
id | 1
-----|----
5001 | 1
5002 | 1
5003 | 1
Here I'm passing static value as a parameter to the SQL query.
In Place of static value I have a list like
A = [1,2,3]
Size of A is same as the number of rows in the table.
List elements may not be in the incremental order they may be in any random order elements may have strings also but we will have a list.
I want replace static parameter with A. So that it will iterate with list and gives the list elements
I need a query which does like below
for example :
select id , A from table;
Result :
id | A
----|-----
5001| 1
5002| 2
5003| 3
Can someone help me in generating this query?
Try this:
mysql> select id from new_table;
+----+
| id |
+----+
| 1 |
| 2 |
+----+
2 rows in set (0.00 sec)
mysql> SET #row_number = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> select id, #row_number := #row_number + 1, ELT(#row_number, '1111', 2222) AS A from new_table;
+----+--------------------------------+------+
| id | #row_number := #row_number + 1 | A |
+----+--------------------------------+------+
| 1 | 1 | 1111 |
| 2 | 2 | 2222 |
+----+--------------------------------+------+
2 rows in set (0.00 sec)

Less value function MySQL

I'm learning basic MYSQL and I want to get only the less value in this query.
Select DATE_FORMAT(CURDATE(),'%Y') - DATE_FORMAT(HIRE_DATE,'%Y') from employees;
Is there any function that do that?
EDIT:
I found the solution by myself. If someone wants to see is like that:
select MIN(timestampdiff(year,HIRE_DATE,CURDATE())) as LessYearsWorking FROM employees;
For getting the least you have a least function
mysql> select least(year(curdate()),year('2014-01-01')) as l;
+------+
| l |
+------+
| 2014 |
+------+
1 row in set (0.04 sec)
And for getting the difference in years between 2 dates you can use timestampdiff
mysql> select timestampdiff(year,'2014-01-01',curdate()) as d ;
+------+
| d |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
Combining both of them and getting the difference and also the least value in the same query could be done as
select
timestampdiff(year,'2014-01-01',curdate()) as d,
least(year(curdate()),year('2014-01-01')) as l ;
So your query becomes
select
timestampdiff(year,HIRE_DATE,curdate()) as diff,
least(year(curdate()),year(HIRE_DATE)) as least_date
from employees

Is it possible to configure MySQL to return TIMESTAMP value as a UNIXTIMESTAMP?

Is it possible to configure MySQL to return TIMESTAMP value as a UNIXTIMESTAMP by default, rather than casting every column in the SELECT statement?
MySQL has a function to convert a date to a unix timestamp.
https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_unix-timestamp
mysql> SELECT UNIX_TIMESTAMP();
-> 1196440210
mysql> SELECT UNIX_TIMESTAMP('2007-11-30 10:30:19');
-> 1196440219
You cannot do that in MySQL configuration.
You can do that on application level - e.g. in PHP, you can use the mysqli_result::fetch_fields() method to detect timestamp type and convert it, other connectors will have similar methods.
Or you can do it - as suggested - using UNIX_TIMESTAMP() function on timestamp columns.
It sounds as though you want a different view of the same data:
mysql> select * from t;
+------+---------------------+
| data | ts |
+------+---------------------+
| foo | 2013-03-19 16:54:45 |
+------+---------------------+
1 row in set (0.00 sec)
mysql> select data, unix_timestamp(ts) from t;
+------+--------------------+
| data | unix_timestamp(ts) |
+------+--------------------+
| foo | 1363712085 |
+------+--------------------+
1 row in set (0.00 sec)
mysql> create view tv (data, time_t) as select data, unix_timestamp(ts) from t;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from tv;
+------+------------+
| data | time_t |
+------+------------+
| foo | 1363712085 |
+------+------------+
1 row in set (0.00 sec)

ANSI 92 Date Difference not working in MySQL

I'm, trying to calculate the number of days between two dates using ANSI SQL standard. But I'm missing something as this statement returns NULL in MySQL.
SELECT EXTRACT(DAY FROM DATE('2009-01-25') - DATE('2009-01-01')) AS day_diff;
I'm aware of the MySQL DATEDIFF function, but I'm curious why this code isn't working.
What am I missing?
Is this what you meant to do?
mysql> SELECT EXTRACT(DAY FROM DATE('2009-01-25')) -
EXTRACT(DAY FROM DATE('2009-01-01')) AS day_diff;
+----------+
| day_diff |
+----------+
| 24 |
+----------+
1 row in set (0.00 sec)
UPDATE:
If you want this to work for dates in different months (or even different years), then you can use the MySQL DATEDIFF() function.
Examples:
mysql> select datediff('2009-04-25','2009-01-01');
+-------------------------------------+
| datediff('2009-04-25','2009-01-01') |
+-------------------------------------+
| 114 |
+-------------------------------------+
1 row in set (0.00 sec)
mysql> select datediff('2010-04-25','2009-01-01');
+-------------------------------------+
| datediff('2010-04-25','2009-01-01') |
+-------------------------------------+
| 479 |
+-------------------------------------+
1 row in set (0.00 sec)