SQL With Query and Union clause - with-statement

Is it possible to have more than one WITH statement and a Union clause? I cannot put IP and PHYS together because it overcounts the membermonth. Even tried a case statement for IP versus PHYS and that did not work. Right now I have several different queries. 1 to write the first WITH to a table. 1 to write the second WITH to temp table and then 1 query that inserts the results of the second WITH to the first WITH table.
Example:
WITH PData as (
SELECT DISTINCT
period, type, MAX(Period) OVER (PARTITION BY type) as MaxPeriod, MemberMonth as MemCnt
FROM
database
WHERE
lob = 'commercial'
AND segmentproduct IN ('Indiv ACA', 'Indiv Legacy', 'Large Group FI-NR', 'Small Grp ACA', 'Small Grp Legacy')
AND servicetype in ('IP')
AND paidthrough IN (SELECT MAX(paidthrough)
FROM database WITH (NOLOCK))
GROUP BY
period, type, MemberMonth
)
SELECT period, type, MemCnt,
Case WHEN period = MaxPeriod
THEN 'Current Period'
ELSE 'Prior Period'
END AS [Prior Current]
FROM PData
Union
WITH PData2 as (
SELECT DISTINCT
period, type, MAX(Period) OVER (PARTITION BY type) as MaxPeriod, MemberMonth as MemCnt
FROM
database
WHERE
lob = 'commercial'
AND segmentproduct IN ('Indiv ACA', 'Indiv Legacy', 'Large Group FI-NR', 'Small Grp ACA', 'Small Grp Legacy')
AND servicetype in ('Phys')
AND paidthrough IN (SELECT MAX(paidthrough)
FROM database WITH (NOLOCK))
GROUP BY
period, type, MemCnt
)
SELECT period, type, MemCnt,
Case WHEN period = MaxPeriod
THEN 'Current Period'
ELSE 'Prior Period'
END AS [Prior Current]
FROM PData2

You can use with once for a query but you can introduce multiple CTEs
The structure you need is
with
pdata as ( <query> ),
pdata2 as ( <query> )
select cols
from pdata
union
select cols
from pdata2;

Related

Error in concatenation of `LISTAGG` function[Not a duplicate question] [duplicate]

I have the following table TEMP
I want to create a pivot view using SQL, Ordered by CATEGORY ASC ,by LEVEL DESC and SET ASC and fill in the value .
Expected output:
I have tried the following code but unable to get a workaround the aggregate part which is throwing an error:
SELECT *
FROM
(SELECT
SET, LEVEL, CATEGORY, VALUE
FROM
TEMP
ORDER BY
CATEGORY ASC, LEVEL DESC, SET ASC) x
PIVOT
(value(VALUE) FOR RISK_LEVEL IN ('X','Y','Z') AND CATEGORY IN ('ABC', 'DEF', 'GHI', 'JKL')) p
Furthermore I want to know if there can be any method for dynamically adding the columns and arriving at this view for any table having the same columns (so that hardcoding can be avoided).
I know we can do this in Excel and transpose it, but I want the data to be stored in the db in this format.
A stored function(or procedure) might be created in order to create a SQL for Dynamic Pivoting, and the result set is loaded into a variable of type SYS_REFCURSOR :
CREATE OR REPLACE FUNCTION Get_Categories_RS RETURN SYS_REFCURSOR IS
v_recordset SYS_REFCURSOR;
v_sql VARCHAR2(32767);
v_cols_1 VARCHAR2(32767);
v_cols_2 VARCHAR2(32767);
BEGIN
SELECT LISTAGG( ''''||"level"||''' AS "'||"level"||'"' , ',' )
WITHIN GROUP ( ORDER BY "level" DESC )
INTO v_cols_1
FROM (
SELECT DISTINCT "level"
FROM temp
);
SELECT LISTAGG( 'MAX(CASE WHEN category = '''||category||''' THEN "'||"level"||'" END) AS "'||"level"||'_'||category||'"' , ',' )
WITHIN GROUP ( ORDER BY category, "level" DESC )
INTO v_cols_2
FROM (
SELECT DISTINCT "level", category
FROM temp
);
v_sql :=
'SELECT "set", '|| v_cols_2 ||'
FROM
(
SELECT *
FROM temp
PIVOT
(
MAX(value) FOR "level" IN ( '|| v_cols_1 ||' )
)
)
GROUP BY "set"
ORDER BY "set"';
OPEN v_recordset FOR v_sql;
RETURN v_recordset;
END;
in which I used two levels of pivoting : the first is within the inner query involving PIVOT Clause, and the second is in the outer query having the conditional aggregation logic. Notice that the order of levels should be in the descending order( Z, Y, X ) within the expected result as conforming to the description.
And then invoke
VAR rc REFCURSOR
EXEC :rc := Get_Categories_RS;
PRINT rc
from SQL Developer's Command Line in order to get the result set
Btw, avoid using reserved keywords such as set and level as in your case. I needed to quote them in order to be able to use.

Teradata macro with volatile table and CTE to insert data into a table

I need to create a teradata macro to extract information into a volatile table first, then do CTE to extract data from this volatile table and insert into a teradata table, tried different ways all fail, appreciate help!
CREATE MACRO database.macro_insertion_tablename AS (
CREATE VOLATILE TABLE vt AS
(
SELECT
id, bu,
CONCAT(TO_CHAR(comment_date, 'yyyy-mm-dd HH24:MI:SS'), ' ', action) AS full_action,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY date DESC) AS row_num,
COUNT(*) OVER (PARTITION BY id) as cnt
FROM database.table1
) WITH DATA UNIQUE PRIMARY INDEX(id, row_num) ON COMMIT PRESERVE ROWS;
WITH RECURSIVE cte (id, bu, act, rn) AS
(
SELECT
id, bu
,CAST(full_action AS VARCHAR(5000)) AS full_action
,row_num
FROM vt
WHERE row_num = cnt
UNION ALL
SELECT
vt.id, vt.bu
,cte.act || ' / ' || vt.full_action
,vt.row_num
FROM vt
JOIN cte On vt.id = cte.id AND vt.row_num = cte.rn - 1
)
INSERT INTO database.table (id, bu, full_action)
SELECT id, bu, act
FROM cte
WHERE rn = 1;
DROP TABLE vt;
);
DDL must be the only statement in a Teradata Macro.
As workaround you could switch to a Global Temporary Table which is defined once and then you simply Insert/Select into it instead of CREATE VOLATILE TABLE.
But in your case there's no need for a temp table plus inefficient recursive processing to get a "group concat":
SELECT id, max(bu) -- maybe min(bu)?
XmlAgg(Concat(To_Char(comment_date, 'yyyy-mm-dd HH24:MI:SS'), ' ', action)
ORDER BY comment_date) (VARCHAR(5000)) AS full_action
FROM database.table1
GROUP BY 1
will give you a similar result.
To follow up on my comments, you should be able to define multiple CTEs in the same statement. It may be tricky getting the RECURSIVE CTE to work, but it sounds like it's possible. Maybe something like this:
CREATE MACRO database.macro_insertion_tablename AS (
WITH vt (id, bu, full_action, row_num, cnt) AS
(
SELECT
id, bu,
CONCAT(TO_CHAR(comment_date, 'yyyy-mm-dd HH24:MI:SS'), ' ', action) AS full_action,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY date DESC) AS row_num,
COUNT(*) OVER (PARTITION BY id) as cnt
FROM database.table1
),
RECURSIVE cte (id, bu, act, rn) AS
(
SELECT
id, bu
,CAST(full_action AS VARCHAR(5000)) AS full_action
,row_num
FROM vt
WHERE row_num = cnt
UNION ALL
SELECT
vt.id, vt.bu
,cte.act || ' / ' || vt.full_action
,vt.row_num
FROM vt
JOIN cte On vt.id = cte.id AND vt.row_num = cte.rn - 1
)
INSERT INTO database.table (id, bu, full_action)
SELECT id, bu, act
FROM cte
WHERE rn = 1;
);
I don't have a Teradata system to test with, so not 100% it will work as-is, but give it a try. You may need to change RECURSIVE to WITH RECURSIVE and also the ordering of the CTE queries (i.e. put the RECURSIVE one first). Take a look at these two links:
Teradata Forum - Multiple With Clause
teradata Forum - Common Table Expressions

substring: row information to field names

I have information in an SQLite database. The database structure can not be changed.
I am trying to construct a query that will give me a result in which the TypeOfInformation entries are field names:
My first try was to work with subqueries:
SELECT (SELECT Value FROM FinData WHERE Type = 'Price') AS Price,
(SELECT Value FROM FinData WHERE Type = 'Volume') AS Volume
FROM FinData")
Seemed perfect, however, the result was a resultset in which EVERY entry in the columns Price and Volume are equal to the FIRST respective entry of Price and Volume in the original database:
I tried to get around this and to include the other Price and Volume information ­- but I failed. (Which is a pity, because the syntax seemed somehow easy to grasp.)
Next try was the following:
Select Date, Value AS Volume From FinData WHERE Volume IN
(SELECT Value FROM FinData WHERE (Type = 'Volume'))
This gives me a resultset with a Volume column and all volume information. Okay, so far. However, when I want to complement this resultset which a Price column via
Select Date, Value AS Volume From FinData WHERE Volume IN
(SELECT Value FROM FinData WHERE (Type = 'Volume'))
union
Select Date, Value AS Close From FinData WHERE Price IN
(SELECT Value FROM FinData WHERE (Type = 'Price'))
I get a resultset that shows Price and Volume information in only ONE column ("Volume"), which therefore is also useless.
To look up a value corresponding to a row in the outer query, you have to use a correlated subquery, which explicitly makes a connection between both:
SELECT Date,
(SELECT Value
FROM FinData
WHERE Date = Dates.Date
AND TypeOfInformation = 'Price'
) AS Price,
(SELECT Value
FROM FinData
WHERE Date = Dates.Date
AND TypeOfInformation = 'Volume'
) AS Volume
FROM (SELECT DISTINCT Date
FROM FinData) AS Dates;
(The DISTINCT subquery is used to prevent multiple rows for each date.)
Alternatively, group all rows for a date, and use aggregation functions and CASE expressions to extract the values from the proper rows:
SELECT Date,
MAX(CASE WHEN TypeOfInformation = 'Price' THEN Value END) AS Price,
MAX(CASE WHEN TypeOfInformation = 'Volume' THEN Value END) AS Volume
FROM FinData
GROUP BY Date;
Assuming dates are unique per price volume pair, you can do this:
with xxx(date,price,volume) as
(
select date,value,0 from findata where typeofinformation = 'Price'
union
select date,0,value from findata where typeofinformation = 'Volume'
)
select date,sum(price) price,sum(volume) volume from xxx group by date;

Simple Split function in SQL Server 2012 with explanation pls

I have two tables Procedures and ProcedureTypes.
Procedures has a column Type which is a varchar with the values (1, 2), (3, 4), (4, 5) etc...
ProcedureType has a primary key 'ID' 1 to 9.
ID Description
1 Drug
2 Other-Drug
etc...
ID is an integer value and Type is varchar value.
Now I need to join these two tables to show the values
ID in the Procedures table
ProcedureType in the Procedures table
Description in the ProceduresType table with the value separated by a "-".
For example if he value in Type is (1,2) the new table after join should show values in the description like (Drug-Other Drug)
I have used this query bot to no avail
SELECT * FROM dbo.[Split]((select RequestType from GPsProcedures), ',')
Can anyone tell me how to do it and why the above query is not working
with Procedures as (
select 1 as ID, '1,2,3' as Typ
),
ProcedureTypes as (
select 1 as TypeID, 'Drug' as Name
union select 2 , 'Other-Drug'
union select 3 , 'Test 3'
)
/*Get one extra column of type xml*/
,Procedures_xml as (
select id,CONVERT(xml,' <root> <s>' + REPLACE(Typ,',','</s> <s>') + '</s> </root> ') as Typ_xml
from Procedures
)
/*Convert the field string to multiple rows then join to procedure types*/
, Procdure_With_Type as (
select ID,T.c.value('.','varchar(20)') as TypeID,
ProcedureTypes.Name
from Procedures_xml
CROSS APPLY Typ_xml.nodes('/root/s') T(c)
INNER JOIN ProcedureTypes ON T.c.value('.','varchar(20)') = ProcedureTypes.TypeID
)
/*Finally, group the procedures type names by procedure id*/
select id,
STUFF((
SELECT ', ' + [Name]
FROM Procdure_With_Type inn
WHERE (Procdure_With_Type.ID = inn.ID)
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
from Procdure_With_Type
group by ID
You can't have a select statement as a parameter for a function, so instead of this:
SELECT * FROM dbo.[Split]((select RequestType from GPsProcedures), ',')
Use this:
select S.*
from GPsProcedures P
cross apply dbo.[Split](P.RequestType, ',') S

How to use case statement with select and group by

select company,
case when ((select count() from ftmuser where active='0' group by company)>5) Then (select count() from ftmuser where active='0' group by company)
ELSE '0'
END
From ftmuser
I want to display only those records having count > 5 but above query fails and says ERROR: more than one row returned by a subquery used as an expression
SQL state: 21000
Please guide me on this.
Thanks.
Try this:
select company, case when ((select count(*) from ftmuser where active='0'
group by company where company=ft.company)>5)
Then (select count() from ftmuser <br>where active='0'
group by company where company=ft.company )
ELSE '0' END
From ftmuser ft

Resources