How to transform a MSSQL CTE query to MySQL? - mysql

in my MySQL schema, I have the category(id, parentid, name) table
In the MSSQL, I have that CTE query (to build a category tree from the bottom up for a supplied category ID:
with CTE (id, pid, name)
as
(
select id, parentid as pid,name
from category
where id = 197
union all
select CTE.pid as id , category.parentid as pid, category.name
from CTE
inner join category
on category.id = CTE.pid
)
select * from CTE
How to 'transform' that query to MySQL ?

Unfortunately MySQL doesn't support CTE (Common Table Expressions). This is long overdue IMO. Often, you can just use a subquery instead, but this particular CTE is recursive: it refers to itself inside the query. Recursive CTE's are extremely useful for hierarchical data, but again: MySql doesn't support them at all. You have to implement a stored procedure to get the same results.
A previous answer of mine should provide a good starting point:
Generating Depth based tree from Hierarchical Data in MySQL (no CTEs)

Thankfully it's not necessary anymore, as MySQL starting from 8.0.1 supports CTE.

unfortunately MYSQl or XAMPP(MARIADB) mysql doesnot support CTEs(COMMON TABLE EXPRESSIONS),
for the same you will have to use nested queries.
for more information click on the below link:-
https://mariadb.com/kb/en/library/with/

Please check what version of MySQL you have using SELECT VERSION();
If it is 8 or above you can then proceed further with my comment.
With MySQL 8.0, MariaDB 10.2, and later versions, you can use recursive CTEs:
WITH RECURSIVE CTE (id, pid, name) AS (
select id, parentid as pid,name
from category
where id = 197
union all
select CTE.pid as id , category.parentid as pid, category.name
from CTE
inner join category
on category.id = CTE.pid
)
select * from CTE ;
Note that CTEs are limited by cte_max_recursion_depth (default 1000, max 4,294,967,295 (2³²−1)) in MySQL and by max_recursive_iterations (default 4,294,967,295) in MariaDB.
You can increase the limit by executing:
SET cte_max_recursion_depth = 4294967295;
It will only affect your current session and won't be persisted.

Related

MySQL Correlated sub query table name out of scope

This form of my correlated sub query comes up with the error message "Unknown column 'Invoices.TranDate' in 'where clause'"
select InvoiceID, TranDate
, ifnull(TotPayments,0) TotPayments, ifnull(CountPayments,0) CountPayments
from Invoices
left join (select DebtorID, sum(TranAmount) TotPayments, count(*) CountPayments
from CashTrans
where CashTrans.TranDate >= Invoices.TranDate
group by DebtorID) PY on PY.DebtorID = Invoices.DebtorID
Yet this version works
select InvoiceID, TranDate
, (select sum(TranAmount) from CashTrans
where CashTrans.TranDate >= Invoices.TranDate
and CashTrans.DebtorID = Invoices.DebtorID) TotPayments
, (select count(*) from CashTrans
where CashTrans.TranDate >= Invoices.TranDate
and CashTrans.DebtorID = Invoices.DebtorID) CountPayments
from Invoices;
What is wrong with the first query? The only thing I can think of is that on my Windows system I have configured lower_case_table_names=2 as I want to preserve mixed case names. Perhaps that has something to do with the first query not seeing Invoice.TranDate in scope? MySQL Documentation and internet searches have not thrown any light on the matter.
https://dev.mysql.com/doc/refman/8.0/en/lateral-derived-tables.html says:
A derived table cannot normally refer to (depend on) columns of preceding tables in the same FROM clause. As of MySQL 8.0.14, a derived table may be defined as a lateral derived table to specify that such references are permitted.
In SQL:1999, the query becomes legal if the derived tables are preceded by the LATERAL keyword (which means “this derived table depends on previous tables on its left side”):
I have not tested it, but I believe your query could be written this way:
SELECT InvoiceID, TranDate,
IFNULL(TotPayments,0) AS TotPayments,
ifnull(CountPayments,0) AS CountPayments
FROM Invoices
LEFT JOIN LATERAL (
SELECT DebtorID,
SUM(TranAmount) AS TotPayments,
COUNT(*) AS CountPayments
FROM CashTrans
WHERE CashTrans.TranDate >= Invoices.TranDate
GROUP BY DebtorID
) AS PY ON PY.DebtorID = Invoices.DebtorID;
Also be aware this requires you to use at least MySQL 8.0.14.

MySQL Query that traverses pointer fields

Due to some legacy code, I have 2 MySQL tables with the below structure (simplified):
Invoice (ID, InvoiceNo, First_Item)
InvoiceItem (ID, Details, Next_Item)
Obviously, there are many InvoiceItems for each Invoice.
The legacy app expects you to load the Invoice row first, then load the first item from the InvoiceItem table using the Invoice's First_Item value. To get each successive InvoiceItem row, you would then follow its Next_Item value until you hit a null value.
Is there a way to write MySQL SQL that would bring back all InvoiceItem(s) for a given Invoice? i.e follow the Invoice's First_Item and then traverse all the Invoice_Items's Next_Item pointers.
Thanks
Bill.
You want do a recursive query , but mysql < 8 does not support it .
This is a solution that work on you small dataset ( from sqlfiddle )
select id,
details ,
next_item
from (select * from invoiceitem
order by id ) inv_itm,
(select #iis := 0 ) init
where find_in_set(id, #iis)
and not find_in_set(9999999999, #iis)
and length(#iis := concat(#iis, ',', ifnull(next_item,9999999999))) ;
This solution will work only if for each invoices id of items are in ascending order .
This solution is inspired by How to create a MySQL hierarchical recursive query
You need to plan a upgrade to 5.7 or 8.0 , because bellow you will have no security update soon .
see https://en.wikipedia.org/wiki/MySQL#Release_history

SQL - Nested query optimization

How can I optimize this query SQL?
CREATE TABLE table1 AS
SELECT * FROM temp
WHERE Birth_Place IN
(SELECT c.DES_COM
FROM tableCom AS c
WHERE c.COD_PROV IS NULL)
ORDER BY Cod, Birth_Date
I think that the problem is the IN clause
First of all it's not quite valid SQL, since you are selecting and sorting by columns that are not part of the group. What you want to do is called "select top N in group", check out Select first row in each GROUP BY group?
Your query doesn't make sense, because you have SELECT * with GROUP BY. Ignoring that, I would recommend writing the query as:
SELECT t.*
FROM temp t
WHERE EXISTS (SELECT 1
FROM tableCom c
WHERE t.Birth_Place = c.DES_COM AND
c.COD_PROV IS NULL
)
ORDER BY Cod, Birth_Date;
For this, I recommend an index on tableCom(desc_com, cod_prov). Your database might also be able to use an an index on temp(cod, birth_date, birthplace).

CTE recursive query in select statement

I have two MySQL server version 8.0, one for local development and another on an Heroku Instance, more precisely on Heroku i'm using a service called JAWSDB.
For my project I have to use the following CTE query, because the structure of the table tree_structure is hierarchical.
The purpose of the query is that for every row in tree_structure I have to get all of its child, and then count how many user in user_roles table are present in that particular row and its child.
SELECT mtr.id,
mtr.parent_id,
mtr.name,
mtr.manager_id,
CONCAT(users.nome, ' ', users.cognome) as resp_name,
(
with recursive cte (id, name, parent_id) as (
select id,
name,
parent_id
from tree_structure as tr_rec
where tr_rec.parent_id = mtr.id
and tr_rec.session_id = '2018'
union all
select tr.id,
tr.name,
tr.parent_id
from tree_structure as tr
inner join cte
on tr.parent_id = cte.id
WHERE tr.session_id = '2018'
)
select count(distinct (user_id))
from user_roles as ur_count
where ur_count.structure_id in (select distinct(id) from cte)
) as utenti
FROM tree_structure as mtr
LEFT JOIN users ON mtr.manager_id = users.id
WHERE level = 0
The problems is that on my local server it works whereas on the heroku instance it gaves me the following error:
unknow columns mtr.id in where clause.
Has someone any ideas of what is causing this error?
Thanks in advance and sorry for my bad english.
You have an ambiguous table reference in the CTE:
SELECT
....
(with recursive cte (id, name, parent_id) as (
....
from tree_structure as tr_rec -- here you have aliased the table
where tr_rec.id = tree_structure.id -- here you refer to the table and its alias
and tr_rec.session_id = '2018'
union all
....
)
....
) as utenti
....
Table tree_structure is used in the subselect and in the outermost select. The good practice is to make an unique alias for every table reference you have used.
Also you have a typo in the condition that should check self-referencing of the hierarcy root node:
where tr_rec.id = tr_rec.parent_id
and tr_rec.session_id = '2018'
OK guys I found out why the query was wrong. Apparently since MySQL version 8.0.14 they introduced support for using external parameters within subqueries.
My local version was 8.0.16 but the online version was 8.0.11 so because of this my query didn't work.

substitute of count(*) OVER () in mysql

this is my SQL server query
` Select top 8
count(id) OVER () as speccount,
(SELECT count(*) From Profile_Master pm WHERE CONCAT(pm.fname , pm.lname) LIKE '%'+#keyword+'%' and
(select Top 1 city from DAddress_Master where profileid=pm.id)=#city) as drcount,
(SELECT count(*) From DAddress_Master dam WHERE dam.city=#city and dam.clinicname LIKE '%'+#keyword+'%') as cliniccount,
specialization AS name,
id as id,
'Specialization' as type,
'' as drspec
From Specialization_Master
Where specialization LIKE '%'+#keyword+'%'`
actually part of my whole query. now i have to convert this query into mysql, which not that much big deal, but can some one tell substitute of count(*) OVER () in mysql?
I came across to this functions.
SELECT SQL_CALC_FOUND_ROWS
and SELECT FOUND_ROWS();
but is there another way??
The easiest way is probably a subquery:
select (select count(*) from Specialization where specialization LIKE '%'+#keyword+'%'`
) as speccnt
Note: if you are switching databases and you are using functionality such as window functions, you might consider Postgres rather than MySQL, because Postgres supports window functions.