Create recursive CTE for this table [duplicate] - sql

This question already has answers here:
The maximum recursion 100 has been exhausted before statement completion
(2 answers)
Closed 3 months ago.
I have a table like this:
|id |name |parent|
+-------+----------+------+
|1 |iran | |
|2 |iraq | |
|3 |tehran |1 |
|4 |tehran |3 |
|5 |Vaiasr St |4 |
|6 |Fars |1 |
|7 |shiraz |6 |
It's about addresses from country to street. I want to create address by recursive cte like this:
with cte_address as
(
select
ID, [Name], parent
from
[Address]
where
Parent is null
union all
select
a.ID, a.[name], a.Parent
from
address a
inner join
cte_address c on a.parent = c.id
)
select *
from cte_address
But I get an error:
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.

you have to use option (maxrecursion 0) at the end of your select query,Maxrecursion 0 allows infinite recursion:
with cte_address as
(
...
...
)
select * from cte_address
option (maxrecursion 0)
Note :
Limiting the number of recursions allowed for a specific query in SQL Server with the 100 default value prevents the cause of an infinite loop situation due to a poorly designed recursive CTE query.

Related

Looking for Postgres query which can provide output like MongoDB group by function

Product table
|_id|name |
|---|------|
|3 |Laptop|
Size table
|_id|product_id|size|
|---|----------|----|
|5 |3 |15 |
|6 |3 |17 |
Query:
select tp._id, tp.name, ts.size from test_product tp
left join test_size ts on tp._id = ts.product_id
group by tp._id, tp.name, ts.size
where tp._id = 3 limit 10 offset 0
Current output:
|_id|name |size|
|---|------|----|
|3 |Laptop|15 |
|3 |Laptop|17 |
Expected output
|_id|name |size |
|---|------|-------|
|3 |Laptop|[15,17]|
Note:
Due to current query I'm getting 2 record for the same product and my limit and offset query logic is getting false and not getting proper count. I'm not well aware of Postgres queries for this kind of situation. So I need solution for this so my limit and offset logic will be correct for fetching data and for this query my count of product will be 1.
Use array_agg():
SELECT
tp._id,
tp.name,
ARRAY_AGG(ts.size ORDER BY ts.size) -- ORDER BY to get consistent results
FROM
test_product tp
LEFT JOIN test_size ts ON tp._id = ts.product_id
GROUP BY
tp._id,
tp.name
WHERE
tp._id = 3
LIMIT 10
OFFSET 0;
The ORDER BY within the aggregation is optional, but it's always nice to get consistent results over and over again.

How can I apply mutliple filters on my sql tables

I am struggling with what looks to be relational division in sql. I need to filter data from the same table based on multiple criteria. Here-under is a schema of what my table looks like.
| -------|----------------|-----------------|----------------|
|ID |Question |RespondentId |Answer |
| -------|----------------|-----------------|----------------|
|1 |Big |1 |Yes |
|2 |Big |2 |Yes |
|3 |Big |3 |No |
|4 |Gender |1 |Male |
|5 |Gender |2 |Female |
|6 |Gender |3 |Female |
|7 |Children |1 |No |
|8 |Children |2 |Yes |
|9 |Children |3 |No |
--------------------------------------------------------------
I need the RespondenIds from this table called Answers that match the following filters : Question = Big and Children and Answer = Yes and Yes respectively for every question. Therefore, if I would have a correct Sql query my result should return me the following array : [2] since the only row that has the answer Yes for the question Big and the answer Yes for the question Children is the one with RespondentId = 2.
Also, the questions and the answers provided are not fixed and should be modular. For instance, I should be able to change an answer or removing a question without having to change the whole structure of my query.
Could you please help me finding a correct query for this problem ? I have been looking to a lot of explanations provided by #Erwin Brandstetter but none of them match my needs.
I would do this as:
select a.RespondentId
from Answers a
when (question, answer) in ( ('Big', 'Yes'), ('Children', 'Yes') )
group by RespondentId
having count(*) = 2 ;
This is easily generalized to:
with qa as (
select v.*
from (values ('Big', 'Yes'), ('Children', 'Yes')
) v(question, answer)
select a.RespondentId
from Answers a join
qa
on a.question = qa.question and a.answer = qa.answer
group by RespondentId
having count(*) = (select count(*) from qa);
This is pretty generalizable. You could even arrange the CTE to take an array or json argument and parse out into the separate comparison values.
You could check for result that having a count = 2 for the rows that match the question and answer where condition
select RespondentId
from Answers
when question in ( 'Big', 'Children')
and Answer ='Yes'
group by RespondentId having count(*) = 2
I think what you are looking for is to Pivot the table. There are different syntaxes for different databases. You effectively turn the values of the "Question" column into columns of their own and then look for the rows matching your criteria.
Here is an inefficient example in standard SQL where I create one table for each question and join them into a single table using the RespondentId.
select respondent_id from
(select * from answers where question = 'Big') as big
join
(select * from answers where question = 'Children') as children
on
big.respondent_id = children.respondent_id
where
big.answer = 'Yes'
and
children.answer = 'Yes';

How to Group by with sum of multi row in another table

I have two tables. Ticket and TicketBasket as following image.
I want to select somthings like this:
|tId|tDate|customerName|expDate|tax|total|
|1 |xxx |xxx      |xxx  |2 |25 |
|2 |xxx |xxx      |xxx  |2 |20 |
|3 |xxx |xxx      |xxx  |2 |15 |
because of my sql command, the result has iteration with tId because each Ticket can have multi Items in it's basket.
my sql code is :
SELECT distinct Ticket.tId,dbo.ToCustomeDate(Ticket.tDate) 'tDate',
Ticket.customerName,dbo.ToCustomeDate(isnull(Ticket.expDate,Ticket.tDate)) 'expDate',
Ticket.tax,
(
(((TicketBasket.gamePrice*TicketBasket.gameCount)-
(((TicketBasket.gamePrice * TicketBasket.gameCount)*TicketBasket.offPrice)/100))+
(((TicketBasket.gamePrice * TicketBasket.gameCount)-
(((TicketBasket.gamePrice * TicketBasket.gameCount)*TicketBasket.offPrice)/100))* (Ticket.tax)/100))
) AS total
FROM Ticket right JOIN TicketBasket
ON Ticket.tId = TicketBasket.tId
but the result is
|tId|tDate|customerName|expDate|tax|total|
|1 |xxx |xxx      |xxx  |2 |10 |
|1 |xxx |xxx      |xxx  |2 |10 |
|1 |xxx |xxx      |xxx  |2 |5  |
|2 |xxx |xxx      |xxx  |2 |10 |
|2 |xxx |xxx      |xxx  |2 |10 |
|3 |xxx |xxx      |xxx  |2 |10 |
|3 |xxx |xxx      |xxx  |2 |5  |
I can handle this with cursor but I know it's a heavy load to execute the select query, so thanks for other solutions.
This should work..based on your query's output:
Select
iq.tld, iq.tDate, iq.customerName, iq.expDate,iq.tax,
SUM(total) as newTotal
from
(
SELECT distinct Ticket.tId,dbo.ToCustomeDate(Ticket.tDate) 'tDate',
Ticket.customerName,dbo.ToCustomeDate(isnull(Ticket.expDate,Ticket.tDate)) 'expDate',
Ticket.tax,
(
(((TicketBasket.gamePrice*TicketBasket.gameCount)-
(((TicketBasket.gamePrice * TicketBasket.gameCount)*TicketBasket.offPrice)/100))+
(((TicketBasket.gamePrice * TicketBasket.gameCount)-
(((TicketBasket.gamePrice * TicketBasket.gameCount)*TicketBasket.offPrice)/100))* (Ticket.tax)/100))
) AS total
FROM Ticket right JOIN TicketBasket
ON Ticket.tId = TicketBasket.tId
)iq
group by iq.tld, iq.tDate, iq.customerName, iq.expDate,iq.tax

Iterating over groups in table

I have the following data:
cte1
===========================
m_ids |p_id |level
---------|-----------|-----
{123} |98 |1
{123} |111 |2
{432,222}|215 |1
{432,222}|215 |1
{432,222}|240 |2
{432,222}|240 |2
{432,222}|437 |3
{432,222}|275 |3
I have to perform the following operation:
Extract p_id by the following algorithm
For every row with same m_ids
In each group:
2.I. Group records by p_id
2.II. Order desc records by level
2.III. Select p_id with exact count as the m_ids length and with the biggest level
So far I fail to write this algorithm completely, but I wrote (probably wrong where I'm getting array_length) this for the last part of it:
SELECT id
FROM grouped_cte1
GROUP BY id,
level
HAVING Count(*) = array_length(grouped_cte1.m_ids, 1)
ORDER BY level DESC
LIMIT 1
where grouped_cte1 for m_ids={123} is
m_ids |p_id |level
---------|-----------|-----
{123} |98 |1
{123} |111 |2
and for m_ids={432,222} is
m_ids |p_id |level
---------|-----------|-----
{432,222}|215 |1
{432,222}|215 |1
{432,222}|240 |2
{432,222}|240 |2
{432,222}|437 |3
{432,222}|275 |3
etc.
2) Combine query from p.1 with the following. The following extracts p_id with level=1 for each m_ids:
select m_ids, p_id from cte1 where level=1 --also selecting m_ids for joining later`
which results in the following:
m_ids |p_id
---------|----
{123} |98
{432,222}|215
Desirable result:
m_ids |result_1 |result_2
---------|-----------|--------
{123} |111 |98
{432,222}|240 |215
So could anyone please help me solve the first part of algorithm and (optionally) combine it in a single query with the second part?
EDIT: So far I fail at:
1. Breaking the presented table into subtables by m_ids while iterating over it.
2. Performing computation of array_length(grouped_cte1.m_ids, 1) for corresponding rows in query.
For the first part of the query you're on the right track, but you need to change the grouping logic and then join again to the table to filter it out by highest level per m_ids for which you could use DISTINCT ON clause combined with proper sorting:
select
distinct on (t.m_ids)
t.m_ids, t.p_id, t.level
from cte1 t
join (
select
m_ids,
p_id
from cte1
group by m_ids, p_id
having count(*) = array_length(m_ids, 1)
) as g using (m_ids, p_id)
order by t.m_ids, t.level DESC;
This would give you:
m_ids | p_id | level
-----------+------+-------
{123} | 111 | 2
{432,222} | 240 | 2
And then when combined with second query (using FULL JOIN for displaying purposes, when the first query is missing such conditions) which I modified by adding distinct since there can be (and in fact is) more than one record for m_ids, p_id pair with first level it would look like:
select
coalesce(r1.m_ids, r2.m_ids) as m_ids,
r1.p_id AS result_1,
r2.p_id AS result_2
from (
select
distinct on (t.m_ids)
t.m_ids, t.p_id, t.level
from cte1 t
join (
select
m_ids,
p_id
from cte1
group by m_ids, p_id
having count(*) = array_length(m_ids, 1)
) as g using (m_ids, p_id)
order by t.m_ids, t.level DESC
) r1
full join (
select distinct m_ids, p_id
from cte1
where level = 1
) r2 on r1.m_ids = r2.m_ids
giving you result:
m_ids | result_1 | result_2
-----------+----------+----------
{123} | 111 | 98
{432,222} | 240 | 215
that looks different from what you've expected but from my understanding of the logic it is the correct one. If I misunderstood anything, please let me know.
Just for the sake of logic explanation, one point:
Why m_ids with {123} returns 111 for result_1?
for group of m_ids = {123} we have two distinct p_id values
both 98 and 111 account for the condition of equality count with the m_ids length
p_id = 111 has a higher level, thus is chosen for the result_1

Fetch data from multiple tables in postgresql

I am working on an application where I want to fetch the records from multiple tables which are connected through foreign key. The query I am using is
select ue.institute, ue.marks, uf.relation, uf.name
from user_education ue, user_family uf where ue.user_id=12 and uf.user_id=12
The result of the query is
You can see the data is repeating in it. I only want a record one time. I want no repetition. I want something like this
T1 T2
id|name|fid id|descrip| fid
1 |A |1 1|DA | 1
2 |B |1 2|DB | 1
2 |B |1
Result which I want:
Result:
id|name|fid|id|descrip| fid
1 |A |1 |1|DA | 1
2 |B |1 |2|DB | 1
2 |B |1 |
The results fetched through your query
The total rows are 5
More Information
I want the rows of same user_id from both tables but you can see in T1 there are 3 rows and in T2 there are 2 rows. I do not want repetitions but also I want to fetch all the data on the basis of user_id
Table Schemas,s
T1
T2
I can't see why you would want that, but the solution could be to use the window function row_number():
SELECT ue.institute, ue.marks, uf.relation, uf.name
FROM (SELECT institute, marks, row_number() OVER ()
FROM user_education
WHERE user_id=12) ue
FULL OUTER JOIN
(SELECT relation, name, row_number() OVER ()
FROM user_family
WHERE user_id=12) uf
USING (row_number);
The result would be pretty meaningless though, as there is no ordering defined in the individual result sets.