Use row value from CTE in function - sql

I'm trying to use row values from CTE expressions to pass to a function.
Something like:
WITH
start_number(x) as (VALUES(1)),
end_number(y) as (VALUES(10)),
z AS (SELECT * FROM generate_series(start_number.x, end_number.y))
SELECT z.*
What's the syntax?
Notes: start_number & end_number may be the result of a query, typically returning a single row.

You need to reference the two CTEs in the FROM clause:
WITH start_number(x) as (
VALUES(1)
), end_number(y) as (
VALUES(10)
)
SELECT *
FROM start_number, end_number, generate_series(start_number.x, end_number.y))
You can simplify that by using only a single CTE for the numbers:
WITH params(start_number, end_number) as (
VALUES (1, 10)
)
SELECT *
FROM params p, generate_series(p.start_number, p.end_number))

You need a from clause so you can get x and y:
WITH start_number(x) as (
VALUES (1)
),
end_number(y) as (
VALUES (10)
),
z AS (
SELECT gs.z
FROM start_number CROSS JOIN
end_number CROSS JOIN LATERAL
generate_series(start_number.x, end_number.y) gs(z)
)
SELECT z.*
FROM z;
Merely defining a CTE does not make it available to subsequent code and CTEs in the query. You need to include the CTE in a FROM clause.
In addition, you should be in the habit of naming the columns in CTEs. In your code z has a column with no name.
The above is one method. You could also use subqueries as well:
WITH start_number(x) as (
VALUES(1)
),
end_number(y) as (
VALUES(10)
),
z AS (
SELECT *
FROM generate_series((SELECT x FROM start_number), (SELECT y FROM end_number))
)
SELECT z.*
FROM z;
Here is a db<>fiddle.

Related

Should SQL CTE's hold constant values?

I came across an interesting piece of SQL code in a view today where a CTE was being used to hold constant values used within sub-queries. It made raise an eyebrow as I've never seen this practice before, though I can understand the thought process behind wanting to reduce the duplicate occurrences of a string value . I'm more of a .Net guy, so I would love to hear thoughts from some SQL folks on whether this pattern is a good or bad practice.
WITH myConstants AS (
SELECT 'myValue' AS myValue
)
SELECT
.......
(SELECT .......
WHERE x.myValue = mc.myValue
) AS mySubQuery
FROM
.......
INNER JOIN myConstants mc ON 1=1
You could use a CTE to store a constant in this way for scoping as a kind of "local variable" (though I've never seen one used for this purpose). After you've completed a statement using the CTE that holds the constant, you can't access it later within the same batch (unless your statement stored it somewhere else). A variable on the other hand, you could access repeatedly throughout the same batch.
e.g.
DECLARE #myVar VARCHAR(20) = 'test';
WITH myConstants AS (
SELECT 'myValue' AS myValue
)
SELECT myValue
FROM myConstants
-- can't access myConstants CTE or its content at this point
-- but can access the variable within the same batch...
SELECT #myVar AS MyVar
-- ...as many times as needed
SELECT #myVar AS MyVar
GO
I use a CTE for constant values all the time. And YES, you can use the CTE values just like you would a variable/parameter. Note this query:
WITH x(c1,c2,c3) AS (SELECT 1,2,3)
SELECT x.c2 AS xyz FROM x
UNION ALL
SELECT x.c1 FROM x
EXCEPT
SELECT x.c3 FROM x;
Returns:
xyz
----
2
1
The nuance is that you must include a reference to the CTE each time you do. Each time I "call" a "constant" I must include FROM x.
In short, a CTE is simply cleaner syntax for a subquery. The main difference between the two is that CTEs can be recursive, subqueries can't be. Subqueries, on the other hand, can be correlated. Note these three queries that produce the exact same results and exact same execution plan:
DECLARE #sometable TABLE (SomeId INT);
INSERT #sometable VALUES(1),(1),(3),(4),(5);
-- CTE
WITH myConstants(c1,c2,c3) AS
(
SELECT 1, 2, 3
)
SELECT t.SomeId, Total = COUNT(*)
FROM #sometable AS t
CROSS JOIN myConstants AS m
WHERE t.SomeId IN (c1,c2,c3)
GROUP BY t.SomeId;
-- Subquery
SELECT t.SomeId, Total = COUNT(*)
FROM #sometable AS t
CROSS JOIN (SELECT 1,2,3) AS myConstants(c1,c2,c3)
WHERE t.SomeId IN (c1,c2,c3)
GROUP BY t.SomeId;
-- VALUES Constructor
SELECT t.SomeId, Total = COUNT(*)
FROM #sometable AS t
CROSS JOIN (VALUES(1,2,3)) AS myConstants(c1,c2,c3)
WHERE t.SomeId IN (c1,c2,c3)
GROUP BY t.SomeId;
The benefit of a CTE vs a subquery is when you need to perform nesting. Consider this query (which uses the same temp table from above):
SELECT t.SomeId
FROM
(
SELECT t.SomeId
FROM #sometable AS t
WHERE t.SomeId < 10
) AS logic1
JOIN #sometable AS t
ON logic1.SomeId = t.SomeId
UNION ALL
SELECT TOP(2) logic3.SomeId
FROM
(
SELECT logic2.SomeId
FROM
(
SELECT t.SomeId
FROM
(
SELECT t.SomeId
FROM #sometable AS t
WHERE t.SomeId < 10
) AS logic1
JOIN #sometable AS t
ON logic1.SomeId = t.SomeId
) AS logic2
) AS logic3;
This can be simplified like so:
WITH
logic1 AS
(
SELECT t.SomeId
FROM #sometable AS t
WHERE t.SomeId < 10
),
logic2 AS
(
SELECT t.SomeId
FROM logic1 AS m
JOIN #sometable AS t
ON t.SomeId = m.SomeId
),
logic3 AS
(
SELECT TOP(2) m.SomeId
FROM logic2 AS m
)
SELECT m.SomeId
FROM logic2 AS m
UNION ALL
SELECT m.SomeId
FROM logic3 AS m;
Again, both return the exact same results and produce the exact same execution plan.

select with multiple condition MAX

How to select data with multiple condition max
SELECT * FROM [TEMP_TBL]
WHERE "FLDR_NUM" =(SELECT MAX(FLDR_NUM)
FROM [TEMP_TBL]) AND "DNLD_DT" =(SELECT MAX(DNLD_DT)
FROM [TEMP_TBL)
you can use cte to reuse your max() value.
with cte(maxflr) as (
select max(fldr_num) as maxflr from TEMP_TBL
)select * from TEMP_TBL
cross apply cte
where "FLDR_NUM" = cte.maxflr and "DNLD_DT" = cte.maxflr

How To Create Duplicate Records depending on Column which indicates on Repetition

I've got a table which consisting aggregated records, and i need to Split them according to specific column ('Shares Bought' like in the example below), as Follow:
Original Table:
Requested Table:
Needless to say, that there are more records like that in the table and i need an automated query (not manual insertions),
and also there are some more attributes which i will need to duplicate (like the field 'Date').
You would need to first generate_rows with increasing row_number and then perform a cross join with your table.
Eg:
create table t(rowid int, name varchar(100),shares_bought int, date_val date)
insert into t
select *
from (values (1,'Dan',2,'2018-08-23')
,(2,'Mirko',1,'2018-08-25')
,(3,'Shuli',3,'2018-05-14')
,(4,'Regina',1,'2018-01-19')
)t(x,y,z,a)
with generate_data
as (select top (select max(shares_bought) from t)
row_number() over(order by (select null)) as rnk /* This would generate rows starting from 1,2,3 etc*/
from sys.objects a
cross join sys.objects b
)
select row_number() over(order by t.rowid) as rowid,t.name,1 as shares_bought,t.date_val
from t
join generate_data gd
on gd.rnk <=t.shares_bought /* generate rows up and until the number of shares bought*/
order by 1
Here is a db fiddle link
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=5736255585c3ab2c2964c655bec9e08b
declare #t table (rowid int, name varchar(100), sb int, dt date);
insert into #t values
(1, 'Dan', 2, '20180823'),
(2, 'Mirco', 1, '20180825'),
(3, 'Shuli', 3, '20180514'),
(4, 'Regina', 1, '20180119');
with nums as
(
select n
from (values(1), (2), (3), (4)) v(n)
)
select t.*
from #t t
cross apply (select top (t.sb) *
from nums) a;
Use a table of numbers instead of CTE nums or add there as many values as you can find in Shares Bought column.
Other option is to use recursive cte :
with t as (
select 1 as RowId, Name, ShareBought, Date
from table
union all
select RowId+1, Name, ShareBought, Date
from t
where RowId <= ShareBought
)
select row_number() over (order by name) as RowId,
Name, 1 as ShareBought, Date
from t;
If the sharebought not limited to only 2 or 3 then you would have to use option (maxrecursion 0) query hint as because by default it is limited to only 100 sharebought.

Presto create table with 'with' queries

typically to create a table in Presto (from existing db tables), I do:
create table abc as (
select...
)
But to make my code simple, I've broken out subqueries like this:
with sub1 as (
select...
),
sub2 as (
select...
),
sub3 as (
select...
)
select
from sub1 join sub2 on ...
join sub3 on ...
Where do I put the create table statement here? The actual query is more complex than the above so I am trying to avoid having to put the subqueries within the main query.
This is possible with an INSERT INTO not sure about CREATE TABLE:
INSERT INTO s1
WITH q1 AS (...)
SELECT * FROM q1
Maybe you could give this a shot:
CREATE TABLE s1 as
WITH q1 AS (...)
SELECT * FROM q1
If Strings are concerned, then following works
WITH sample AS (
SELECT * FROM (VALUES ('strA', 'strB'), ('strC', 'strD'), ('strE', 'strF')) AS account (name, cat)
)
SELECT name, cat from sample;
if integers are only concerned values , then following works: -
WITH slab (SNo,Amount) AS (VALUES (1,1000),(2,2000),(3,3000),(4,4000),(5,5000),(6,6000),(7,7000),(8,8000),(9,9000),(10,10000),(11, 11000),(12,12000),(13,13000),(14,14000),(15,15000),(16,16000),(17,17000),(18,18000),(19,19000),(20,20000),(21,21000),(22,22000),(23,23000),(24,24000),(25,25000),(26,26000),(27,27000),(28,28000),(29,29000),(30,30000),(31,31000),(32,32000),(33,33000),(34,34000),(35,35000),(36,36000),(37,37000),(38,38000),(39,39000),(40,40000),(41,41000),(42,42000),(43,43000),(44,44000),(45,45000),(46,46000),(47,47000),(48,48000),(49,49000),(50,50000),(51,51000)
)
SELECT * FROM slab;
Syntax is just as if you prepend create table .. as to the select. E.g. the following worked for me on Presto 0.170:
create table memory.default.a as
with w as (
select * from (values 1) t(x)
)
select * from w;
(I use experimental memory connector so that this is copy-pastable to try it out.)
I believe you need to 'wrap' the entire query like this:
create table EXAMPLE as (
with sub1 as (
select ...
),
.......
select
from sub1....
)
This works
create table answertable as
(
select * from (
WITH
q as ( select findcode from (values 'G463','G464','G465','G466','G467','G468','I64','I694') as concept(findcode) )
select s.*
from table1 s
inner join q on starts_with(s.xxx, q.findcode)
where s.pk is not null
)
)

Using a WITH inside of a RECURSIVE WITH in PostgreSQL [duplicate]

This question already has answers here:
Issue with recursive CTE in PostgreSQL
(1 answer)
How to use multiple CTEs in a single SQL query?
(4 answers)
Closed 9 years ago.
In PostgreSQL, a WITH can be used by another WITH, for example:
WITH num AS (
VALUES (50)
), num2 AS (
SELECT column1 * 2 AS value FROM num
)
SELECT value FROM num2;
And then there are RECURSIVE WITHs, which can be done like so:
WITH RECURSIVE t(n) AS (
VALUES (1)
UNION ALL
SELECT n+1 FROM t WHERE n < 100
)
SELECT sum(n) FROM t;
But so far, I have not found a way for a RECURSIVE WITH to use a previous WITH. I would think that it should be something like this:
WITH num AS (
VALUES (50)
), num2 AS (
SELECT column1 * 2 AS value FROM num
), RECURSIVE t(n) AS (
VALUES (1)
UNION ALL
SELECT n+1 FROM t WHERE n < (SELECT * FROM num2)
)
SELECT sum(n) FROM t;
But this does not work. So is there a way to do this? If so, how?
Start with WITH RECURSIVE. You can still add non-recursive CTEs:
WITH RECURSIVE
num AS (VALUES (50))
, num2 AS (SELECT column1 * 2 AS value FROM num)
, t(n) AS (
VALUES (1)
UNION ALL
SELECT n+1 FROM t WHERE n < (SELECT value FROM num2)
)
SELECT sum(n) FROM t;
sqlfiddle
See:
How to use multiple CTEs in a single SQL query?