Cte within Cte in SQL - sql

I have been encountered with a situation where I need to apply a where, group by condition on the result of CTE in the CTE.
Table 1 as follows
+---+---+---+---+
| x | y | z | w |
+---+---+---+---+
| 1 | 2 | 3 | 1 |
| 2 | 3 | 4 | 2 |
| 3 | 2 | 5 | 3 |
| 1 | 2 | 6 | 2 |
+---+---+---+---+
Table 2 as follows
+---+---+-----+---+
| a | b | c | d |
+---+---+-----+---+
| 1 | m | 100 | 1 |
| 2 | n | 23 | 2 |
| 4 | o | 34 | 4 |
| 1 | m | 23 | 2 |
+---+---+-----+---+
Assuming I have the data of following sql query in a table called TAB
with cte as (
select x,y,z from table1),
cte1 as (select a,b,c from table2)
select cte.x,cte1.y,cte1.z,cte2.b,cte2.c from cte left join cte1 on cte.x=cte.a and cte1.w=cte2.d
Result of above CTE would be as follows
+---+---+---+---+---+-----+
| x | y | z | w | b | c |
+---+---+---+---+---+-----+
| 1 | 2 | 3 | 1 | m | 100 |
| 2 | 3 | 4 | 2 | n | 23 |
| 1 | 2 | 6 | 2 | m | 23 |
+---+---+---+---+---+-----+
I would like to query the following from the table TAB
select * from TAB where (X||b) in (select (X||b) from TAB group by (X||Y) having sum(c)=123)
I'm trying to formulate the SQL query as follows but it's not as i expected:
select * from (
with cte as (
select x,y,z from table1),
cte1 as (select a,b,c from table2)
select cte.x,cte1.y,cte1.z,cte2.b,cte2.c from cte left join cte1 on cte.x=cte.a) as TAB
where ((X||b) in (select (X||b) from TAB group by (X||Y) having sum(c)=123))
The final result must be as follows
+---+---+---+---+---+-----+
| x | y | z | w | b | c |
+---+---+---+---+---+-----+
| 1 | 2 | 3 | 1 | m | 100 |
| 1 | 2 | 6 | 2 | m | 23 |
+---+---+---+---+---+-----+

I don't think DB2 allows CTEs in subqueries or to be nested. Why not just write this using another CTE?
with cte as (
select x,y,z from
table1
),
cte1 as (
select a,b,c
from table2
),
tab as (
select cte.x,cte1.y,cte1.z,cte1.w,cte2.b,cte2.c
from cte left join
cte1
on cte.x=cte.a and cte1.w=cte2.d
)
select *
from TAB
where (X||b) in (select (X||b) from TAB group by (X||Y) having sum(c)=123);

Related

Select Tree Structure from two tables

let us assume I have Table A
| PK | Name |
-------------
| 1 | AA |
| 2 | BB |
| 3 | CC |
and table B
| PK | FK | Value |
-------------------
| 1 | 1 | i |
| 2 | 1 | j |
| 3 | 2 | x |
| 4 | 2 | y |
| 5 | 3 | l |
| 6 | 3 | k |
how can I select the below result
| PK | Name |
-------------
| 1 | AA |
| 1 | i |
| 2 | j |
| 2 | BB |
| 3 | x |
| 4 | y |
| 3 | CC |
| 3 | l |
| 4 | k |
List parents and under each parent list its children
Many Thanks for help
Very interesting table design. I believe it's just a matter of unioning your data and then ordering your results how you want them. If it's only a single level child-parent relationship, this should work just fine.
If Object_Id('tempdb..#TableA') Is Not Null Drop Table #TableA;
If Object_Id('tempdb..#TableB') Is Not Null Drop Table #TableB;
Select * Into #TableA
From (Values (1,'AA'),(2,'BB'),(3,'CC')) As a(PK,[Name])
Select * Into #TableB
From (Values (1,1,'i'),(2,1,'j'),(3,2,'x'),(4,2,'y'),(5,3,'l'),(6,3,'k')) As a(PK,FK,[Value])
;With Cte
As
(
Select PK,[Name],PK As OrderID,0 As LevelID /*Used to ensure parents are put above children*/
From #TableA
Union All
Select PK,[Value],FK,1
From #TableB
)
Select [PK], [Name]
From Cte
Order By OrderID,LevelID
Results:
PK | Name
1 | AA
1 | i
2 | j
2 | BB
3 | x
4 | y
3 | CC
5 | l
6 | k
Note: My last two rows(l and k) are a bit different than results. I assumed you it was typo when you put 3 and 4 as the id's rather than 5 and 6

Sum all sub group last value by group

Consider the following table:
ID | ITEM | GROUP_ID | VAL | COST
---+------+----------+-----------+-------
1 | A | 1 | 1 | 12
2 | B | 1 | 2 | 12
3 | C | 1 | 3 | 12
4 | D | 1 | 4 | 13
5 | D | 1 | 5 | 12
6 | E | 2 | 1 | 17
7 | E | 2 | 2 | 10
8 | E | 2 | 3 | 11
9 | E | 2 | 4 | 12
10 | F | 2 | 5 | 15
11 | F | 2 | 6 | 13
12 | F | 2 | 7 | 11
13 | F | 2 | 8 | 12
how to get the result as follow:
GROUP_ID | VAL | COST
----------+-----------+-------
1 | 15 | 48
2 | 36 | 24
The val is the sum by group id.
The cost is the sum of last value by item.
Use analytic function ROW_NUMBER() on postgres, oracle or sql server
SqlFiddleDemo
WITH last_item as (
SELECT group_id, sum(cost) as sum_cost
FROM (
SELECT t.*,
ROW_NUMBER() over (partition by item order by id desc) as rn
FROM Table1 t
) as t
WHERE rn = 1
GROUP BY t.group_id
),
val_sum as (
SELECT t.group_id, SUM(val) as sum_val
FROM Table1 t
GROUP BY t.group_id
)
SELECT v.group_id, v.sum_val, l.sum_cost
FROM val_sum v
INNER JOIN last_item l
ON v.group_id = l.group_id
OUTPUT
| group_id | sum_val | sum_cost |
|----------|---------|----------|
| 1 | 15 | 48 |
| 2 | 36 | 24 |
Try this
WITH LastRow (id)
AS (
SELECT MAX(id)
FROM TheTable
GROUP BY item, group_id
)
SELECT group_Id, SUM(val), SUM(CASE WHEN B.id IS NULL THEN 0 ELSE cost END)
FROM TheTable A
LEFT OUTER JOIN LastRow B ON A.id = B.id
GROUP BY group_id
EDIT:
SQL Fiddle Demo
Thanks #Juan Carlos Oropeza for creating the SQL Fiddle test data

group by top two results based on order

I have been trying to get this to work with some row_number, group by, top, sort of things, but I am missing some fundamental concept. I have a table like so:
+-------+-------+-------+
| name | ord | f_id |
+-------+-------+-------+
| a | 1 | 2 |
| b | 5 | 2 |
| c | 6 | 2 |
| d | 2 | 1 |
| e | 4 | 1 |
| a | 2 | 3 |
| c | 50 | 4 |
+-------+-------+-------+
And my desired output would be:
+-------+---------+--------+-------+
| f_id | ord_n | ord | name |
+-------+---------+--------+-------+
| 2 | 1 | 1 | a |
| 2 | 2 | 5 | b |
| 1 | 1 | 2 | d |
| 1 | 2 | 4 | e |
| 3 | 1 | 2 | a |
| 4 | 1 | 50 | c |
+-------+---------+--------+-------+
Where data is ordered by the ord value, and only up to two results per f_id. Should I be working on a Stored Procedure for this or can I just do it with SQL? I have experimented with some select TOP subqueries, but nothing has even come close..
Here are some statements to create the test table:
create table help(name varchar(255),ord tinyint,f_id tinyint);
insert into help values
('a',1,2),
('b',5,2),
('c',6,2),
('d',2,1),
('e',4,1),
('a',2,3),
('c',50,4);
You may use Rank or DENSE_RANK functions.
select A.name, A.ord_n, A.ord , A.f_id from
(
select
RANK() OVER (partition by f_id ORDER BY ord asc) AS "Rank",
ROW_NUMBER() OVER (partition by f_id ORDER BY ord asc) AS "ord_n",
help.*
from help
) A where A.rank <= 2
Sqlfiddle demo

Sql: Aggregation First() After Order by and Group by

id | name | value | time |
--------------------------
1 | A | 1 | 1 |
2 | B | 2 | 2 |
3 | C | 2 | 3 |
4 | A | 3 | 3 |
5 | A | 4 | 2 |
and I expected the result as below:
name | value |
--------------
A | 3 |
B | 2 |
C | 2 |
The results are to show name and value which are lastest time and not duplicate with name.
And I try to query:
SELECT name,First(value)
FROM
(SELECT name,value,time
FROM test
ORDER BY time DESC
)
GROUP BY name;
But I got this result:
name | value |
--------------
A | 1 |
B | 2 |
C | 2 |
I don't understand why A value isn't 3 because from subselect I got A values are 3,4,1 respectively.
Query:
SQLFIDDLEExample
SELECT t.name,
(SELECT t1.value
FROM test t1
WHERE t1.name = t.name
ORDER BY t1.time DESC
LIMIT 1) AS value
FROM test t
GROUP BY t.name
Result:
| NAME | VALUE |
----------------
| A | 3 |
| B | 2 |
| C | 2 |
also you can use partitionby
;with cte as (
select id, row_number() over (order by time desc) rn
from test
)
select * from test
join cte on test.id = cte.id and rn = 1
just choose the one which is faster

SQL::Self join a table to satisfy a particular condition?

I have the following table:
mysql> SELECT * FROM temp;
+----+------+
| id | a |
+----+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
+----+------+
I am trying to get the following output:
+----+------+------+
| id | a | a |
+----+------+------+
| 1 | 1 | 2 |
| 2 | 2 | 3 |
| 3 | 3 | 4 |
+----+------+------+
but I am having a small problem. I wrote the following query:
mysql> SELECT A.id, A.a, B.a FROM temp A, temp B WHERE B.a>A.a;
but my output is the following:
+----+------+------+
| id | a | a |
+----+------+------+
| 1 | 1 | 2 |
| 1 | 1 | 3 |
| 2 | 2 | 3 |
| 1 | 1 | 4 |
| 2 | 2 | 4 |
| 3 | 3 | 4 |
+----+------+------+
Can someone tell me how to convert this into the desired output? I am trying to get a form where only the consecutive values are produced. I mean, if 2 is greater than 1 and 3 is greater than 2, I do not want 3 is greater than 1.
Option 1: "Triangular Join" - Quadratic Complexity
SELECT A.id, A.a, MIN(B.a) AS a
FROM temp A
JOIN temp B ON B.a>A.a
GROUP BY A.id, A.a;`
Option 2: "Pseudo Row_Number()" - Linear Complexity
select a_numbered.id, a_numbered.a, b_numbered.a
from
(
select id,
a,
#rownum := #rownum + 1 as rn
from temp
join (select #rownum := 0) r
order by id
) a_numbered join (
select id,
a,
#rownum2 := #rownum2 + 1 as rn
from temp
join (select #rownum2 := 0) r
order by id
) b_numbered
on b_numbered.rn = a_numbered.rn+1