Could some explain this TSQL code for me? - sql

I got the following stored procedure, but I am new to T-SQL and not quite sure about its syntax. Could some explain a little to me?
Question:
What does the #tmp syntax stand for?
Where is the c in c.RootCategoryId defined?
Here's the code:
ALTER PROCEDURE [dbo].[GetAllTopCategories]
#prewin7 bit
AS
SELECT * INTO #tmp FROM dbo.fnGetCategories(#prewin7)
SELECT
c.RootCategoryId CategoryId,
c.CategoryName + ' [' + CAST(g.DiagnosticCount AS nvarchar(max)) + ']' CategoryName,
#prewin7 as PreWin7
FROM (
SELECT
c.RootCategoryId CategoryId,
SUM(c.DiagnosticCount) DiagnosticCount
FROM #tmp c
GROUP BY c.RootCategoryId
) g
INNER JOIN #tmp c ON c.CategoryId = g.CategoryId
ORDER BY c.CategoryName
DROP TABLE #tmp

The #tmp is a local temporary table, e.g. it is only ever visible to the connection that created it, and once that connection is gone, so is the temporary table. So that DROP TABLE #tmp at the end is really not necessary - the table would be dropped automatically by SQL Server.
This temporary table was created here:
SELECT * INTO #tmp FROM dbo.fnGetCategories(#prewin7)
so it will contain any of the rows that the result set from the dbo.fnGetCategories stored function will return.
SELECT
c.RootCategoryId CategoryId,
SUM(c.DiagnosticCount) DiagnosticCount
FROM #tmp c
Here, data is selected from that temporary table, which has a table alias of c - so therefore, that c.RootCategoryId must be a column of that temporary table, and thus it must be one of the rows that is returned from the stored function called above.

Do you really need a temp table?
ALTER PROCEDURE [dbo].[GetAllTopCategories]
#prewin7 bit
AS
/*SELECT * INTO #tmp FROM dbo.fnGetCategories(#prewin7)*/
SELECT
c.RootCategoryId CategoryId,
c.CategoryName + ' [' + CAST(g.DiagnosticCount AS nvarchar(max)) + ']' CategoryName,
#prewin7 as PreWin7
FROM (
SELECT
c.RootCategoryId CategoryId,
SUM(c.DiagnosticCount) DiagnosticCount
FROM (select * FROM dbo.fnGetCategories(#prewin7)) c
GROUP BY c.RootCategoryId
) g
INNER JOIN (select * FROM dbo.fnGetCategories(#prewin7)) c ON c.CategoryId = g.CategoryId
ORDER BY c.CategoryName

Related

Best way to join this into a temp table and inner join

Just wondering the best way to put this into a temp table and then join it.
IF EXISTS(SELECT LocId
FROM dbo.Locations WITH (NOLOCK)
WHERE SourceSystem = #SourceSystem
AND LocId IN (SELECT ListVal
FROM etopsuser.fnParseListToTable(#LocIdList, ';')) AND IsHot = 1)
BEGIN
Specifically trying to do it on this line of code
(SELECT ListVal
FROM etopsuser.fnParseListToTable(#LocIdList, ';')) AND IsHot = 1)
The NOLOCK is unrelated
You would create a temporary table just like any other table from a select:
SELECT ListVal
INTO #templist
FROM etopsuser.fnParseListToTable(#LocIdList, ';');
Then you would use it as:
SELECT l.LocId
FROM dbo.Locations l JOIN
#templist tl
ON l.LocId = tl.Listval
WHERE l.SourceSystem = #SourceSystem AND l.IsHot = 1
The best way to pass a list into a procedure is to use a Table Valued Parameter
CREATE TYPE dbo.List AS TABLE (ListVal varchar(255));
IF EXISTS(SELECT 1
FROM dbo.Locations l
WHERE l.SourceSystem = #SourceSystem
AND l.LocId IN (
SELECT ll.ListVal
FROM #LocIdList ll
) AND IsHot = 1)
Notes: Always use table references on every column, especially if subqueries are involved. Never use NOLOCK unless you are prepared for incorrect results. EXISTS ignores its SELECT, so SELECT 1 or SELECT NULL is standard.
Then you can pass in the table variable either from client code depending on language, or in T-SQL like this
DECLARE #list dbo.List;
INSERT #list (ListVal)
VALUES ('SomeValue');
EXEC YourProc #LocIdList = #list;

SQL query with "not in" is very slow

I have the following SQL query which is very slow. How can I write the script differently?
select
pws_name
from
pws_asset ass
join
Account acc on acc.AccountId = ass.pws_AccountId
where
acc.AccountNumber in ('188012', '172146', '214727', '13636', '201194', '280294', '34328')
and ass.pws_name not in ('1018684', '1018784', '1019584', '1019784', '1019884', '1070838', '1277139', '1277339'.........)
Kindly follow below steps, that will help in query performance.
Step 1: DECLARE two variables
DECLARE #AccNumList VARCHAR(4000)
DECLARE #PwsNameList VARCHAR(4000)
SET #AccNumList = '188012,172146,214727,13636,201194,280294,34328'
SET #PwsNameList = '1018684,1018784,1019584,1019784,1019884,1070838,1277139,1277339'
Step 2: Create two different temporary tables.
1 for account numbers
Create table #tblAcNum(AccountNumber VARCHAR(50))
2 for pws_name not needed
Create table #tblPwsNameNotNeeded(pws_name VARCHAR(50))
Step 3: Add records in above two tables which are used in IN and NOT IN.
Please check this Split csv string using XML in SQL Server for reference.
INSERT INTO #tblAcNum(AccountNumber)
SELECT
l.value('.','VARCHAR(50)') AcNum
FROM
(
SELECT CAST('<a>' + REPLACE(#AccNumList,',','</a><a>') + '</a>' AS XML) AcNumXML
) x
CROSS APPLY x.AcNumXML.nodes('a') Split(l)
INSERT INTO #tblPwsNameNotNeeded(pws_name)
SELECT
l.value('.','VARCHAR(50)') pws_name
FROM
(
SELECT CAST('<a>' + REPLACE(#PwsNameList,',','</a><a>') + '</a>' AS XML) PwsNameXML
) x
CROSS APPLY x.PwsNameXML.nodes('a') Split(l)
Step 3: INNER JOIN #tblAcNum table with account table with accountnumber column
Step 4: Use NOT EXISTS() function for pws_name not needed like below
WHERE NOT EXISTS
(
SELECT 1
FROM #tblPwsNameNotNeeded pn
Where pn.pws_name = ass.pws_name
)
Step 5: Drop temporary tables after your select query.
DROP TABLE tblAcNum;
DROP TABLE #tblPwsNameNotNeeded;
Please check below query.
DECLARE #AccNumList VARCHAR(4000)
DECLARE #PwsNameList VARCHAR(4000)
SET #AccNumList = '188012,172146,214727,13636,201194,280294,34328'
SET #PwsNameList = '1018684,1018784,1019584,1019784,1019884,1070838,1277139,1277339'
Create table #tblAcNum(AccountNumber VARCHAR(50))
Create table #tblPwsNameNotNeeded(pws_name VARCHAR(50))
INSERT INTO #tblAcNum(AccountNumber)
SELECT
l.value('.','VARCHAR(50)') AcNum
FROM
(
SELECT CAST('<a>' + REPLACE(#AccNumList,',','</a><a>') + '</a>' AS XML) AcNumXML
) x
CROSS APPLY x.AcNumXML.nodes('a') Split(l)
INSERT INTO #tblPwsNameNotNeeded(pws_name)
SELECT
l.value('.','VARCHAR(50)') pws_name
FROM
(
SELECT CAST('<a>' + REPLACE(#PwsNameList,',','</a><a>') + '</a>' AS XML) PwsNameXML
) x
CROSS APPLY x.PwsNameXML.nodes('a') Split(l)
select
ass.pws_name
from pws_asset ass
join Account acc on acc.AccountId = ass.pws_AccountId
INNER JOIN #tblAcNum an ON an.AccountNumber = acc.AccountNumber
WHERE NOT EXISTS
(
SELECT 1
FROM #tblPwsNameNotNeeded pn
Where pn.pws_name = ass.pws_name
)
DROP TABLE tblAcNum;
DROP TABLE #tblPwsNameNotNeeded;
Try this:
; with cte_excludepws as
(select AccountId from pws_asset where pws_name not in ('1018684', '1018784', '1019584', '1019784', '1019884', '1070838', '1277139', '1277339'.........))
select
pws_name
from
pws_asset ass
join
Account acc on acc.AccountId = ass.pws_AccountId
where ass.AccountId not in (select AccountId from cte_excludepws)
and acc.AccountNumber in ('188012', '172146', '214727', '13636', '201194', '280294', '34328')
Alternatively if you can - take the AccountID's into a temporary table instead of cte and create an index on it.
First, be sure that the account numbers are really strings. If they are numbers, drop the single quotes!
Then, for this query
select a.pws_name
from pws_asset a join
Account ac
on ac.AccountId = a.pws_AccountId
where ac.AccountNumber in ('188012', '172146', '214727', '13636', '201194', '280294', '34328') and
a.pws_name not in ('1018684', '1018784', '1019584', '1019784', '1019884', '1070838', '1277139', '1277339'.........);
I would recommend indexes on: account(accountNumber, AccountId) and pws_asset(pws_AccountId, pws_name).

Columns not showinng in DataSet for dynamic SQL

I have the following SP which works correctly when ran on its own:
ALTER PROCEDURE [dbo].[sgetInvoiceHeaderDetails]
#InvoiceNo varchar(max)
AS
BEGIN
SET FMTONLY ON;
declare #sql varchar(max)
set #sql = 'SELECT IH.InvoiceNo, IH.InvoiceDate, IH.InvoiceTime, C.Name, R.Name AS Customer, IH.NetAmount,
IM.Name AS Item, ID.UnitPrice, ID.Qty, ID.Total, ID.BatchNo
FROM InvoiceHeader AS IH INNER JOIN
InvoiceDetail AS ID ON IH.InvoiceNo = ID.InvoiceNo INNER JOIN
Customer AS C ON IH.CustomerId = C.Id INNER JOIN
Route AS R ON IH.RouteId = R.Id INNER JOIN
ItemMaster AS IM ON ID.ItemMasterId = IM.Id
WHERE IH.InvoiceNo IN ('+#InvoiceNo+')'
print #sql
exec (#sql)
END
The problem I'm having is that when I add a DataSet for a report, it pulls no fields/columns in the Fields section. I'm guessing it's due to the dynamic SQL?
How can I resolve that?
As statet in my comment you should avoid the dynamic approach.
Just to offer you a pure inline solution in SQL have a look at this:
DECLARE #tbl TABLE(ID INT, Caption VARCHAR(100));
INSERT INTO #tbl VALUES(1,'Test 1'),(2,'Test 2'),(3,'Test 3'),(4,'Test 4'),(5,'Test 5');
DECLARE #WantToGet VARCHAR(100)='1,3,4';
WITH Splitted AS
(
SELECT CAST('<x>' + REPLACE(#WantToGet,',','</x><x>') + '</x>' AS XML) AS AsXml
)
,SplittedAsList AS
(
SELECT The.Node.value('.','int') As ID
FROM Splitted
CROSS APPLY AsXml.nodes('/x') AS The(Node)
)
SELECT Caption
FROM #tbl AS tbl
INNER JOIN SplittedAsList sal ON sal.ID = tbl.ID;
The string 1,3,4 is splitted as a list. The INNER JOIN at the end is exactly the same as you wanted to achieve with the IN-clause.
This approach you can plcae within a table valued function (make sure to keep this as inline function!). This function is much better reusable everywhere.
The second recommandable approach would be the CREATE TYPE, bute this needs more action on application side...

Is there a way to find all invalid columns that are referenced in a view using SQL Server 2012?

I have inherited a large database project with thousands of views.
Many of the views are invalid. They reference columns that no longer exist. Some of the views are very complex and reference many columns.
Is there an easy way to track down all the incorrect columns references?
This answer finds the underlying columns that were originally defined in the views by looking at sys.views, sys.columns and sys.depends (to get the underlying column if the column has been aliased). It then compares this with the data held in INFORMATION_Schema.VIEW_COLUMN_USAGE which appears to have the current column usage.
SELECT SCHEMA_NAME(v.schema_id) AS SchemaName,
OBJECT_NAME(v.object_id) AS ViewName,
COALESCE(alias.name, C.name) As MissingUnderlyingColumnName
FROM sys.views v
INNER JOIN sys.columns C
ON C.object_id = v.object_id
LEFT JOIN sys.sql_dependencies d
ON d.object_id = v.object_id
LEFT JOIN sys.columns alias
ON d.referenced_major_id = alias.object_id AND c.column_id= alias.column_id
WHERE NOT EXISTS
(
SELECT * FROM Information_Schema.VIEW_COLUMN_USAGE VC
WHERE VIEW_NAME = OBJECT_NAME(v.object_id)
AND VC.COLUMN_NAME = COALESCE(alias.name, C.name)
AND VC.TABLE_SCHEMA = SCHEMA_NAME(v.schema_id)
)
For the following view:
create table test
( column1 varchar(20), column2 varchar(30))
create view vwtest as select column1, column2 as column3 from test
alter table test drop column column1
The query returns:
SchemaName ViewName MissingUnderlyingColumnName
dbo vwtest column1
This was developed with the help of this Answer
UPDATED TO RETRIEVE ERROR DETAILS
So this answer gets you what you want but it isn't the greatest code.
A cursor is used (yes I know :)) to execute a SELECT from each view in a TRY block to find ones that fail. Note I wrap each statement with a SELECT * INTO #temp FROM view X WHERE 1 = 0 this is to stop the EXEC returning any results and the 1=0 is so that SQL Server can optimize the query so that it is in effect a NO-OP.
I then return a list of any views whose sql has failed.
I haven't performed lots of testing on this, but it appears to work. I would like to get rid of the execution of each SELECT from View.
So here it is:
DECLARE curView CURSOR FOR
SELECT v.name AS ViewName
FROM sys.views v
INNER JOIN sys.sql_modules m
on v.object_id = m.object_id
OPEN curView
DECLARE #viewName SYSNAME
DECLARE #failedViews TABLE
(
FailedViewName SYSNAME,
ErrorMessage VARCHAR(MAX)
)
FETCH NEXT FROM curView
INTO #ViewName
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN TRY
exec ('SELECT * INTO #temp FROM ' + #viewName + ' WHERE 1=0' )
END TRY
BEGIN CATCH
INSERT INTO #failedViews VALUES (#viewName, ERROR_MESSAGE())
END CATCH
FETCH NEXT FROM curView
INTO #ViewName
END
CLOSE curView
DEALLOCATE curView
SELECT *
FROM #failedViews
An example of an ERROR returned is:
FailedViewName ErrorMessage
--------------- -------------
vwtest Invalid column name 'column1'.
You could use system tables get information.
SELECT v.VIEW_NAME,v.TABLE_CATALOG,v.TABLE_SCHEMA,v.TABLE_NAME,v.COLUMN_NAME
from INFORMATION_SCHEMA.VIEW_COLUMN_USAGE v
left outer join INFORMATION_SCHEMA.COLUMNS c
ON v.TABLE_CATALOG=c.TABLE_CATALOG AND v.TABLE_SCHEMA=c.TABLE_SCHEMA AND v.TABLE_NAME=c.TABLE_NAME AND v.COLUMN_NAME=c.COLUMN_NAME
WHERE c.TABLE_NAME IS NULL
ORDER BY v.VIEW_NAME

Dynamic sql using table variable -TSQL

My problem is using a table variable in a exec.
declare #sort_col nvarchar(1000) = 'itm_id'
declare #sort_dir nvarchar(4) = 'desc'
declare #filters nvarchar(1000) = ' and itm_name like ''%aa%'''
declare #temp table
(
itm_id int
)
insert into #temp
EXEC('select itm_id from Tblitm where itm_name not like ''%aa%''')
EXEC('select * from (select (ROW_NUMBER() OVER (ORDER BY '+#sort_col+' '+#sort_dir+')) row_num, * FROM (select itm_id, itm_name,
dbo.fnItmsHistory(itm_id) itm_history
from dbo.Tblitm as itm
left outer join '+#temp+' as temp on itm.itm_id = temp.itm_id
where itm_id=itm_id and temp.itm_id = null '+#filters+') as x) as tmp')
It says Must declare the scalar variable "#temp" when the temp table is declared i tried using original temp table and it worked, but i had problems when trying to update my entity model.So is there any solution for this problem?
Note:
I must use exec because in filters i store string for the where clause.
Try moving the table variable inside the dynamic statement.
EXEC('
declare #temp table
(
itm_id int
)
insert into #temp
select itm_id from Tblitm where itm_name not like ''%aa%''
select * from (select (ROW_NUMBER() OVER (ORDER BY '+#sort_col+' '+#sort_dir+')) row_num, * FROM (select itm_id, itm_name,
dbo.fnItmsHistory(itm_id) itm_history
from dbo.Tblitm as itm
left outer join #temp as temp on itm.itm_id = temp.itm_id
where itm_id=itm_id and temp.itm_id = null '+#filters+') as x) as tmp')
For solution i had to use a temp table and then on the start of my stored procedure i used the if condition from the EF can't infer return schema from Stored Procedure selecting from a #temp table anwser.
It's the best solution for this scenario i think.