dynamically create and load table from select query - tsql

SELECT
MEM_ID, [C1],[C2]
from
(select
MEM_ID, Condition_id, condition_result
from tbl_GConditionResult
) x
pivot
(
sum(condition_result)
for condition_id in ([C1],[C2])
) p
The above query returns three columns of data. Until runtime I will not know how many columns in the select statement. Is it possible to load the data from the select statement into a dynamically created table? After processing the data from the dynamically created table I want to drop the table.
Thank you for your help.
Smith

Yes, do a SELECT INTO e.g.
SELECT
MEM_ID, [C1],[C2]
into #TEMP
from
(select
MEM_ID, Condition_id, condition_result
from tbl_GConditionResult
) x
pivot
(
sum(condition_result)
for condition_id in ([C1],[C2])
) p
-- Do what you need with the TEMP table
DROP TABLE #TEMP

Related

T-SQL - Pivot/Crosstab - variable number of values

I have a simple data set that looks like this:
Name Code
A A-One
A A-Two
B B-One
C C-One
C C-Two
C C-Three
I want to output it so it looks like this:
Name Code1 Code2 Code3 Code4 Code...n ...
A A-One A-Two
B B-One
C C-One C-Two C-Three
For each of the 'Name' values, there can be an undetermined number of 'Code' values.
I have been looking at various examples of Pivot SQL [including simple Pivot sql and sql using the XML function?] but I have not been able to figure this out - or to understand if it is even possible.
I would appreciate any help or pointers.
Thanks!
Try it like this:
DECLARE #tbl TABLE([Name] VARCHAR(100),Code VARCHAR(100));
INSERT INTO #tbl VALUES
('A','A-One')
,('A','A-Two')
,('B','B-One')
,('C','C-One')
,('C','C-Two')
,('C','C-Three');
SELECT p.*
FROM
(
SELECT *
,CONCAT('Code',ROW_NUMBER() OVER(PARTITION BY [Name] ORDER BY Code)) AS ColumnName
FROM #tbl
)t
PIVOT
(
MAX(Code) FOR ColumnName IN (Code1,Code2,Code3,Code4,Code5 /*add as many as you need*/)
)p;
This line
,CONCAT('Code',ROW_NUMBER() OVER(PARTITION BY [Name] ORDER BY Code)) AS ColumnName
will use a partitioned ROW_NUMBER in order to create numbered column names per code. The rest is simple PIVOT...
UPDATE: A dynamic approach to reflect the max amount of codes per group
CREATE TABLE TblTest([Name] VARCHAR(100),Code VARCHAR(100));
INSERT INTO TblTest VALUES
('A','A-One')
,('A','A-Two')
,('B','B-One')
,('C','C-One')
,('C','C-Two')
,('C','C-Three');
DECLARE #cols VARCHAR(MAX);
WITH GetMaxCount(mc) AS(SELECT TOP 1 COUNT([Code]) FROM TblTest GROUP BY [Name] ORDER BY COUNT([Code]) DESC)
SELECT #cols=STUFF(
(
SELECT CONCAT(',Code',Nmbr)
FROM
(SELECT TOP((SELECT mc FROM GetMaxCount)) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values) t(Nmbr)
FOR XML PATH('')
),1,1,'');
DECLARE #sql VARCHAR(MAX)=
'SELECT p.*
FROM
(
SELECT *
,CONCAT(''Code'',ROW_NUMBER() OVER(PARTITION BY [Name] ORDER BY Code)) AS ColumnName
FROM TblTest
)t
PIVOT
(
MAX(Code) FOR ColumnName IN (' + #cols + ')
)p;';
EXEC(#sql);
GO
DROP TABLE TblTest;
As you can see, the only part which will change in order to reflect the actual amount of columns is the list in PIVOTs IN() clause.
You can create a string, which looks like Code1,Code2,Code3,...CodeN and build the statement dynamically. This can be triggered with EXEC().
I'd prefer the first approach. Dynamically created SQL is very mighty, but can be a pain in the neck too...

How to optimize SELECT DISTINCT when using multiple Joins?

I have read that using cte's you can speed up a select distinct up to 100 times. Link to the website . They have this following example:
USE tempdb;
GO
DROP TABLE dbo.Test;
GO
CREATE TABLE
dbo.Test
(
data INTEGER NOT NULL,
);
GO
CREATE CLUSTERED INDEX c ON dbo.Test (data);
GO
-- Lots of duplicated values
INSERT dbo.Test WITH (TABLOCK)
(data)
SELECT TOP (5000000)
ROW_NUMBER() OVER (ORDER BY (SELECT 0)) / 117329
FROM master.sys.columns C1,
master.sys.columns C2,
master.sys.columns C3;
GO
WITH RecursiveCTE
AS (
SELECT data = MIN(T.data)
FROM dbo.Test T
UNION ALL
SELECT R.data
FROM (
-- A cunning way to use TOP in the recursive part of a CTE :)
SELECT T.data,
rn = ROW_NUMBER() OVER (ORDER BY T.data)
FROM dbo.Test T
JOIN RecursiveCTE R
ON R.data < T.data
) R
WHERE R.rn = 1
)
SELECT *
FROM RecursiveCTE
OPTION (MAXRECURSION 0);
How would one apply this to a query that has multiple joins? For example i am trying to run this query found below, however it takes roughly two and a half minutes. How would I optimize this accordingly?
SELECT DISTINCT x.code
From jpa
INNER JOIN jp ON jpa.ID=jp.ID
INNER JOIN jd ON (jd.ID=jp.ID And jd.JID=3)
INNER JOIN l ON jpa.ID=l.ID AND l.CID=3
INNER JOIN fa ON fa.ID=jpa.ID
INNER JOIN x ON fa.ID=x.ID
1) GROUP BY on every column worked faster for me.
2) If you have duplicates in some of the tables then you can also pre select that and join from that as an inner query.
3) Generally you can nest join if you expect that this join will limit data.
SQL join format - nested inner joins

insert into temp table without creating it from union results

I have the below query that get results from more than one select.
Now I want these to be in a temp table.
Is there any way to insert these into a temp table without creating the table?
I know how to do that for select
Select * into #s --like that
However how to do that one more than one select?
SELECT Ori.[GeoBoundaryAssId], Ori.[FromGeoBoundaryId], Ori.Sort
From [GeoBoundaryAss] As Ori where Ori.[FromGeoBoundaryId] = (select distinct [FromGeoBoundaryId] from inserted )
Union
SELECT I.[GeoBoundaryAssId], I.[FromGeoBoundaryId], I.Sort
From [inserted] I ;
Add INTO after the first SELECT.
SELECT Ori.[GeoBoundaryAssId], Ori.[FromGeoBoundaryId], Ori.Sort
INTO #s
From [GeoBoundaryAss] As Ori where Ori.[FromGeoBoundaryId] = (select distinct [FromGeoBoundaryId] from inserted )
Union
SELECT I.[GeoBoundaryAssId], I.[FromGeoBoundaryId], I.Sort
From [inserted] I ;
Try this,
INSERT INTO #s ([GeoBoundaryAssId], [FromGeoBoundaryId], Sort)
(
SELECT Ori.[GeoBoundaryAssId], Ori.[FromGeoBoundaryId], Ori.Sort
FROM [GeoBoundaryAss] AS Ori WHERE Ori.[FromGeoBoundaryId] in (SELECT DISTINCT [FromGeoBoundaryId] FROM inserted )
UNION
SELECT I.[GeoBoundaryAssId], I.[FromGeoBoundaryId], I.Sort
FROM [inserted] I
)

PostgreSQL join to denormalize a table with generate_series

I've this table:
CREATE TABLE "mytable"
( name text, count integer );
INSERT INTO mytable VALUES ('john', 4),('mark',2),('albert',3);
and I would like "denormlize" the rows in this way:
SELECT name FROM mytable JOIN generate_series(1,4) tmp(a) ON (a<=count)
so I've a number of rows for each name equals to the count column: I've 4 rows with john, 2 with mark and 3 with albert.
But i can't use the generate_series() function if I don't know the highest count (in this case 4). There is a way to do this without knowing the MAX(count) ?
select name,
generate_series(1,count)
from mytable;
Set returning functions can be used in the select list and will do a cross join with the row retrieved from the base table.
I think this is an undocumented behaviour that might go away in the future, but I'm not sure about that (I recall some discussion regarding this on the mailing list)
SQLFiddle example
DROP TABLE ztable ;
CREATE TABLE ztable (zname varchar, zvalue INTEGER NOT NULL);
INSERT INTO ztable(zname, zvalue) VALUES( 'one', 1), ( 'two', 2 ), ( 'three', 3) , ( 'four', 4 );
WITH expand AS (
WITH RECURSIVE zzz AS (
SELECT 1::integer AS rnk , t0.zname
FROM ztable t0
UNION
SELECT 1+rr.rnk , t1.zname
FROM ztable t1
JOIN zzz rr ON rr.rnk < t1.zvalue
)
SELECT zzz.zname
FROM zzz
)
SELECT x.*
FROM expand x
;

Improve a query with duplicated SELECTS?

I am performing a query that selects two times from the exact same table on 2 different columns and performing a compare with the same set of data twice from another table.
My current method:
DELETE FROM MY_TABLE
WHERE MY_TABLE.BUY_ORDER_ID
IN ( SELECT #tmp_table.order_id FROM #tmp_table )
OR MY_TABLE.SELL_ORDER_ID
IN ( SELECT #tmp_table.order_id FROM #tmp_table )
Is there a way to improve on the query?
Thanks
Possibly. Need to test on your data.
DELETE MY_TABLE
FROM MY_TABLE m
JOIN #tmp_table
on #tmp_table.order_id = m.BUY_ORDER_ID
or #tmp_table.order_id = m.SELL_ORDER_ID
If #tmp_table.order_id is the PK or unique then declare it.
Splitting hairs but maybe
DELETE MY_TABLE
FROM MY_TABLE m
JOIN #tmp_table
on #tmp_table.order_id in ( m.BUY_ORDER_ID, m.SELL_ORDER_ID )
I've tried this on SQL Server and it seems faster. I suppose you can do something similar on sybase?
DELETE FROM MY_TABLE
WHERE EXISTS
(
SELECT * FROM #tmp_table
WHERE
#tmp_table.order_id = MY_TABLE.BUY_ORDER_ID
OR
#tmp_table.order_id = MY_TABLE.SELL_ORDER_ID
)