Scope of Derived Tables in SQL Server - sql

I've been looking into SQL recently and exploring a bit. in regards to Temp Tables I have discovered 3 different temp table types:
1) CREATE TABLE #TempTable
2) DECLARE TABLE #TempTable
3) SELECT * FROM (SELECT * FROM Customers) AS TempTable
Now I understand the scope behind the #TempTable and the #TempTable types, but what about the derived table as in example 3? Where does this derived table get stored? and if it is declared in 1 transaction, can a 2nd transaction access it, or is the scoping of Derived Tables that same as example 1 and 2?

1) CREATE TABLE #TempTable --local temp table
Local temporary tables are visible only in the current session, and can be shared between nested stored procedure calls: http://www.sommarskog.se/share_data.html#temptables
2) DECLARE TABLE #TempTable --local table variable
The scope of a local variable is the batch, stored procedure, or statement block in which it is declared. They can be passed as parameters between procedures. They are not subject to transactions and will retain all rows following a rollback.
3) SELECT * FROM (SELECT * FROM Customers) AS TempTable --derived table
is visible to the current query only
4) CREATE TABLE ##TempTable --global temp table
This differs from a #temp table in that it is visible to all processes. When the creating process ends, the table is removed (but will wait until any current activity from other processes is done).
5) CTE - common table expression
example CTE:
;WITH YourBigCTE AS
(
big query here
)
SELECT * FROM YourTable1 WHERE ID IN (SELECT ID FROM YourBigCTE)
UNION
SELECT * FROM YourTable2 WHERE ID IN (SELECT ID FROM YourBigCTE)
can be used multiple times within the same CTE command, even recursively, and will last for the duration of the CTE command.

This depends on the actual RDBMS you are using - I will assume Sybase or SQL Server (but might well be true for all)
SELECT * FROM (SELECT * FROM Customers) AS TempTable
Temptable is only available for the current SQL statement ie just the select.
SELECT * FROM Customers into tempdb..TempTable
would create a new table in the tempdb whichg would have to be explicitly dropped

Related

`CREATE TABLE AS SELECT FROM` in Oracle Cloud doesn't create a new table

I was trying to create a series of tables in a single SQL query in Oracle Cloud under the ADMIN account. In the minimum script below, RAW_TABLE refers to an existing table.
CREATE TABLE BASE1 AS SELECT * FROM RAW_TABLE;
CREATE TABLE BASE2 AS SELECT * FROM BASE1;
CREATE TABLE BASE3 AS SELECT * FROM BASE2;
SELECT * FROM BASE3
This returns a view of the first 100 rows in BASE3, but it doesn't create the three tables along the way. Did I miss something or is there something peculiar about create table statements in Oracle SQL?
EDIT: The environment is Oracle Database Actions in Oracle Cloud. The three tables would not be available in the list of tables in the database, and doing something like select * from BASE3 in a subsequent query would fail.
CREATE TABLE BASE1 AS SELECT * FROM RAW_TABLE;
CREATE TABLE BASE2 AS SELECT * FROM BASE1;
CREATE TABLE BASE3 AS SELECT * FROM BASE2;
SELECT * FROM BASE3
Above is a valid query sequence for Oracle database. It should have been created three new tables in database. Since it's not happening please do the work in few steps to find out what's wrong.
First please check whether RAW_TABLE is available in database or not. Then try to select data from RAW_TABLE
select * from RAW_TABLE;
If all those are successful then try to create single table with below query:
CREATE TABLE BASE1 AS SELECT * FROM RAW_TABLE;
Hope you would find the problem by then.
DB-Fiddle:
Creating RAW_TABLE and populating data
create table RAW_TABLE (id int, name varchar(50));
insert into RAW_TABLE values (1,'A');
Query to create three more tables ans selecting from the last table:
CREATE TABLE BASE1 AS SELECT * FROM RAW_TABLE;
CREATE TABLE BASE2 AS SELECT * FROM BASE1;
CREATE TABLE BASE3 AS SELECT * FROM BASE2;
SELECT * FROM BASE3
Output:
ID
NAME
1
A
db<>fiddle here
your query fails because you are executing the whole script as one batch and each line is depends on another one , the transactional DBMS's work with blocks of code as one transaction , and that block of code doesn't commit until sql engine can parse and validate the whole block, and since in your block, BASE1 and BASE2 tables doesn't exists just yet , It fails.
so you need to run each statement as a separate batch. either by executing them one by one or in Oracle you can use / as batch separator, like in sql server you can use GO. these commands are not SQL or Oracle commands and are not sent to the database server , they are just break block of code in batches on your client ( like SQL*Plus or shell or SSMS (for Microsoft sql server), so It would look like this:
CREATE TABLE BASE1 AS SELECT * FROM RAW_TABLE;
/
CREATE TABLE BASE2 AS SELECT * FROM BASE1;
/
CREATE TABLE BASE3 AS SELECT * FROM BASE2;
/
SELECT * FROM BASE3
if your client doesn't support that then you only have to run them one by one in separate batches.

SQL Server Global Temporary Table Locking

How do I lock a global temporary table in a stored procedure that's getting created and populated by a SELECT INTO statement? For example:
SELECT *
INTO ##TempEmployee
FROM Employee
This stored procedure is executed for generating reports and it's there in every client database (multi-tenant architecture using different DB per client). I do not want data in this global temporary table to be shared between clients when the report is generated concurrently. I don't have a choice but to use global temp table because I use it for generating columns on the fly using PIVOT.
Why not include it inside a transaction block like
begin transaction
SELECT *
INTO ##TempEmployee
FROM Employee
Try this,
WorkDummySQL
create table rr(id integer,name varchar(20))
insert into rr values(1,'aa')
select * from rr
Tempdb
select * into ##ta from WorkDummySQL.dbo.rr

The multi-part identifier "##ABMPARENT.SYM_INDEX" could not be bound

I'm running the code below in SQL server 2012 and am getting an error msg saying that
"The multi-part identifier "##ABMPARENT.SYM_INDEX" could not be bound."
I've looked through several over related threads which usually result in someone having spelt the table name incorrectly, used the where clause more than once in a single select statement etc ... However I've used the intellisense and have referenced the correct column / table as far as I can see.
Can anybody tell what is causing the error?
Thanks
Ps. I don't have access to save a SQL view which is why I'm using temp tables for this.
IF OBJECT_ID('tempdb..#ABMPARENT') IS NOT NULL
DROP TABLE ##ABMPARENT
IF OBJECT_ID('tempdb..#ALLSYMBOLS') IS NOT NULL
DROP TABLE ##ALLSYMBOLS
SELECT dbo.KLX_PARENT_CHILD.PARENT_NAME ,
dbo.KLX_MASTER_SYMBOL.SYM_INDEX ,
dbo.KLX_MASTER_SYMBOL.SYM_NAME ,
dbo.KLX_SYM_DESC.DESCRIPTION
INTO ##ABMPARENT
FROM dbo.KLX_PARENT_CHILD
INNER JOIN dbo.KLX_MASTER_SYMBOL ON dbo.KLX_PARENT_CHILD.SYM_INDEX = dbo.KLX_MASTER_SYMBOL.SYM_INDEX
INNER JOIN dbo.KLX_SYM_DESC ON dbo.KLX_MASTER_SYMBOL.SYM_INDEX = dbo.KLX_SYM_DESC.MICRO_OBJ_ID
WHERE ( dbo.KLX_PARENT_CHILD.PARENT_NAME = 'ABMRCTREE_1314' )
AND ( dbo.KLX_MASTER_SYMBOL.SYM_NAME LIKE 'LVRC%' )
SELECT dbo.KLX_PARENT_CHILD.PARENT_NAME ,
dbo.KLX_MASTER_SYMBOL.SYM_INDEX ,
dbo.KLX_MASTER_SYMBOL.SYM_NAME ,
dbo.KLX_SYM_DESC.DESCRIPTION ,
dbo.KLX_PARENT_CHILD.PARENT_INDEX
INTO ##ALLSYMBOLS
FROM dbo.KLX_PARENT_CHILD
INNER JOIN dbo.KLX_MASTER_SYMBOL ON dbo.KLX_PARENT_CHILD.SYM_INDEX = dbo.KLX_MASTER_SYMBOL.SYM_INDEX
INNER JOIN dbo.KLX_SYM_DESC ON dbo.KLX_MASTER_SYMBOL.SYM_INDEX = dbo.KLX_SYM_DESC.MICRO_OBJ_ID
SELECT *
FROM ##ALLSYMBOLS
where [##ABMPARENT].SYM_INDEX = ##ALLSYMBOLS.PARENT_INDEX
Your statement SELECT * FROM ##ALLSYMBOLS where [##ABMPARENT].SYM_INDEX = ##ALLSYMBOLS.PARENT_INDEX does not reference a table named [##ABMPARENT], so you cannot reference a column of that table. Did you intend to do a join in that query?
What #Gabe said. Your last select statment is referencing a table that is not in the from clause.
Nothing wrong with temp tables, but why are you using global temp tables (##some-table-name) instead of local temp tables (#some-table-name). Global temp tables are visible to every session, and are, in my experience, a code smell.
Local temp tables are scoped to to your session (or if created within a stored procedure, to a single stored procedure exection. Local temp tables are dropped when the stored procedure that created exits or when the session is closed, whichever comes first. Further, local temp table tables, once created in a stored procedure are visible to nested stored procedures -- e.g., stored procedure A creates temp table #WORK and then invokes stored procedure B which in turn invokes stored procedure C. The temp table #WORK is usable and visible to both stored procedures B and C.
It complicates stored procedure creation though. A stored procedure referencing a temp table that it doesn't create can't be created unless that temp table exists for the connection at the time create procedure is executed, so you need to do something like:
create table #foo ( ... )
go
create proc dbo.A as ...
go
drop table #foo
go
And a code suggestion: use correlation names for your tables, like so:
select *
from dbo.Foo t1
join dbo.Bar t2 on t2.id = t1.bar_id
Your SQL will be way more readable and, you only have to type the long convoluted table name once. You, your coworkers and the physical therapist you've been seeing for repetitive stress syndrome will appreciate it.

Using User Defined Functions and performance?

I'm using stored procedure to fetch data and i needed to filter dynamically. For example if i dont want to fetch some data which's id is 5, 10 or 12 im sending it as string to procedure and im converting it to table via user defined function. But i must consider performance so here is a example:
Solution 1:
SELECT *
FROM Customers
WHERE CustomerID NOT IN (SELECT Value
FROM dbo.func_ConvertListToTable('4,6,5,1,2,3,9,222',','));
Solution 2:
CREATE TABLE #tempTable (Value NVARCHAR(4000));
INSERT INTO #tempTable
SELECT Value FROM dbo.func_ConvertListToTable('4,6,5,1,2,3,9,222',',')
SELECT *
FROM BusinessAds
WHERE AdID NOT IN (SELECT Value FROM #tempTable)
DROP TABLE #tempTable
Which solution is better for performance?
You would probably be better off creating the #temp table with a clustered index and appropriate datatype
CREATE TABLE #tempTable (Value int primary key);
INSERT INTO #tempTable
SELECT DISTINCT Value
FROM dbo.func_ConvertListToTable('4,6,5,1,2,3,9,222',',')
You can also put a clustered index on the table returned by the TVF.
As for which is better SQL Server will always assume that the TVF will return 1 row rather than recompiling after the #temp table is populated, so you would need to consider whether this assumption might cause sub optimal query plans for the case that the list is large.

Using with vs declare a temporary table: performance / difference?

I have created a sql function in SQLServer 2008 that declared a temporary table and uses it to compute a moving average on the values inside
declare #tempTable table
(
GeogType nvarchar(5),
GeogValue nvarchar(7),
dtAdmission date,
timeInterval int,
fromTime nvarchar(5),
toTime nvarchar(5),
EDSyndromeID tinyint,
nVisits int
)
insert #tempTable select * from aces.dbo.fEDVisitCounts(#geogType, #hospID,DATEADD(DD,-#windowDays + 1,#fromDate),
#toDate,#minAge,#maxAge,#gender,#nIntervalsPerDay, #nSyndromeID)
INSERT #table (dtAdmission,EDSyndromeID, MovingAvg)
SELECT list.dtadmission
, #nSyndromeID
, AVG(data.nVisits) as MovingAvg
from #tempTable as list
inner join #tempTable as data
ON list.dtAdmission between data.dtAdmission and DATEADD(DD,#windowDays - 1,data.dtAdmission)
where list.dtAdmission >= #fromDate
GROUP BY list.dtAdmission
but I also found out that you can declare the tempTable like this:
with tempTable as
(
select * from aces.dbo.fEDVisitCounts('ALL', null,DATEADD(DD,-7,'01-09-2010'),
'04-09-2010',0,130,null,1, 0)
)
Question: Is there a major difference in these two approaches? Is one faster than the other or more common / standard? I would think the declare is faster since you define what the columns you are looking for are.. Would it also be even faster if I were to omit the columns that were not used in the calculations of moving average?(not sure about this one since it has to get all of the rows anyways, though selecting less columns makes intuitive sense that it would be faster/less to do)
I also have found a create temporary table #table from here How to declare Internal table in MySQL? but I don't want the table to persist outside of the function (I am not sure if the create temporary table does this or not.)
The #table syntax creates a table variable (an actual table in tempdb) and materialises the results to it.
The WITH syntax defines a Common Table Expression which is not materialised and is just an inline View.
Most of the time you would be better off using the second option. You mention that this is inside a function. If this is a TVF then most of the time you want these to be inline rather than multi statement so they can be expanded out by the optimiser - this would instantly disallow the use of table variables.
Sometimes however (say the underlying query is expensive and you want to avoid it being executed multiple times) you might determine that materializing the intermediate results improves performance in some specific cases. There is currently no way of forcing this for CTEs (without forcing a plan guide at least)
In that eventuality you (in general) have 3 options. A #tablevariable, #localtemp table and a ##globaltemp table. However only the first of these is permitted for use inside a function.
For further information regarding the differences between table variables and #temp tables see here.
In addition to what Martin answered
;with tempTable as
(
select * from aces.dbo.fEDVisitCounts('ALL', null,DATEADD(DD,-7,'01-09-2010'),
'04-09-2010',0,130,null,1, 0)
)
SELECT * FROM tempTable
can also be written like this
SELECT * FROM
(
select * from aces.dbo.fEDVisitCounts('ALL', null,DATEADD(DD,-7,'01-09-2010'),
'04-09-2010',0,130,null,1, 0)
) AS tempTable --now you can join here with other tables
In addition,and correcting to Martin
The #table syntax creates a table variable IN MEMORY
The #Temp syntax creates a table variable in Tempdb
Thats why #tables are faster than #temp tables