Could anyone have idea why my stored procedure encountered an error when I executed it?
I have tried to isolate in a simple select statement and supplying the value in where clause, it works fine. But when I put to stored procedure with a declared parameter, it won't work anymore.
This is the error after I execute the stored procedure:
Msg 137, Level 15, State 2, Line 30
Must declare the scalar variable "#BU".
See below for my stored procedure.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_Planning_GetMonthlyPivotX]
#BU char(7)
AS
BEGIN
DECLARE #PivotColumns AS NVARCHAR(MAX)
SELECT #PivotColumns = COALESCE(#PivotColumns + ',', '') + QUOTENAME([YEARMONTH])
FROM
(SELECT DISTINCT
CONVERT(CHAR(6), CAST([releaseddate] AS DATE), 112) AS [YEARMONTH]
FROM
[dbo].[ShopOrder]) AS PivotQuery
ORDER BY
[YEARMONTH]
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT *
FROM
(SELECT
a.[ItemCode], b.[Description],
B.[Catalog] as [BU], a.[WH],
f.[CustNo], f.[CustName],
b.[IsOutSource],
[dbo].[f_Routings_GetMCHr](a.[ItemCode]) as [Std.Time],
d.[Tonnage], d.[MachineNo],
d.[MachineNo] as [FixedMachine],
[dbo].[f_BOM_GetChildItem](a.ItemCode) as [ChildItem],
[dbo].[f_Item_GetItemDescription]([dbo].[f_BOM_GetChildItem](a.ItemCode)) as [ChildDescription],
[dbo].[f_BOM_GetChildQty](a.ItemCode,[dbo].[f_BOM_GetChildItem](a.ItemCode)) as [Usage],
e.[TRP],
CONVERT(char(6), cast([ReleasedDate] as date),112) as [YearMonthRel],
[dbo].[f_Inventory_GetTotalStock](b.[ItemCode]) as [Inventory],
b.[MinStockLvl] as [SafetyStock],
a.[RequiredQty] as [ShopOrderQty],
a.[FinishedQty] as [ActualQty],
g.[WorkTimeHr] as [AvailableTime]
FROM
[CPS].[dbo].[ShopOrder] AS a with(nolock)
LEFT OUTER JOIN
[CPS].[dbo].[items] AS b with(nolock) ON a.[ItemCode] = b.[ItemCode]
LEFT OUTER JOIN
[CPS].[dbo].[ItemByMachine] AS d with(nolock) ON a.[ItemCode] = d.[ItemCode]
LEFT OUTER JOIN
[CPS].[dbo].[MCTonnage] as e with(nolock) ON d.[Tonnage] = e.[Tonnage]
LEFT OUTER JOIN
[CPS].[dbo].[Customer] as f with(nolock) ON b.[CustNo] = f.[CustNo]
LEFT OUTER JOIN
[CPS].[dbo].[Machine] as g with(nolock) ON d.[MachineNo] = g.[MachineNo]
WHERE
b.[Catalog] = #BU) AS T
PIVOT
(
SUM([ShopOrderQty])
FOR [YearMonthRel] IN (' + #PivotColumns + ')
) p order by [Tonnage],[MachineNo],[ItemCode];'
Execute SP_EXECUTESQL #query
END
should be
WHERE b.[Catalog] = ''' + #BU + '''
and your select should be
SET #query = N'SELECT * FROM
as nvarchar
Related
I am trying to use #Temp tables in my dynamic SQL in Microsoft SQL Server 2014, and it works fine locally, but if I put it inside an OPENROWSET so I can gather data from another server, it gets error:
OLE DB provider "SQLNCLI11" for linked server "(null)" returned
message "Deferred prepare could not be completed.". Msg 8180, Level
16, State 1, Line 9 Statement(s) could not be prepared. Msg 208, Level
16, State 1, Line 9 Invalid object name '#RefmdCodeList'
Is that just not allowed? Or is there some different way I need to do it?
To simplify the issue, here are two examples.
THIS WORKS:
declare #sql nvarchar(max)
set #sql = N' select * from #RefmdCodeList'
exec(#sql)
THIS DOES NOT WORK:
declare #sql nvarchar(max)
set #sql = N' SELECT * FROM
OPENROWSET(''SQLNCLI'',
''Server=' + #PAFServer + ';Database=' +#PAFDatabase +';UID=p2kservices;PWD=P0werP#th!'',
N'' SELECT * from #RefmdCodeList'')'
exec(#sql)
For people who might ask, below is what I'm really trying to do. If I remove the #Temp tables, my code as written below runs fine. With the #Temp tables included, I get error message above.
declare #sql nvarchar(max)
set #sql = N' SELECT * FROM OPENROWSET(''SQLNCLI'', ''Server=' + #PAFServer + ';Database=' +#PAFDatabase +';UID=p2kservices;PWD=P0werP#th!'',
N'' ;WITH spec_data (entity, accession_no, acc_id, spec_id, spec_description, spec_label, spec_sc_code) AS
(SELECT ''''PAMF'''' AS entity, a.accession_no, a.id, asp.id, asp.description, asp.specimen_label, substring(''''88''''+spec_sc.code, 1,5)
FROM accession_2 a (NOLOCK)
JOIN acc_specimen asp (nolock) ON asp.acc_id = a.id
LEFT OUTER JOIN acc_charges spec_ac (NOLOCK) on spec_ac.acc_id = asp.acc_id
AND spec_ac.rec_id = asp.id AND spec_ac.rec_type = ''''S''''
LEFT OUTER JOIN service_code spec_sc (NOLOCK) on spec_sc.id = spec_ac.service_code_id
JOIN acc_refmd aref (NOLOCK) ON aref.acc_id = a.id
WHERE recv_date >= ''''+CONVERT(VARCHAR(10),#SDate, 101) +'''' and recv_date < ''''+CONVERT(VARCHAR(10),#EDate, 101)+''''
AND aref.refmd_id in (SELECT id from #RefmdCodeList)
)
SELECT DISTINCT entity accession_no, asp.acc_id, spec_id, spec_description, spec_label,
spec_sc_code, substring(''''88''''+lab_sc.code, 1,5)
FROM spec_data asp
LEFT OUTER JOIN acc_charges lab_ac (NOLOCK) on lab_ac.acc_id = asp.acc_id and rec_type = ''''L''''
JOIN service_code lab_sc (NOLOCK) on lab_sc.id = lab_ac.service_code_id
JOIN acc_order ord (NOLOCK) on ord.id = lab_ac.rec_id
AND ord.acc_specimen_id = asp.spec_id
WHERE substring(''''88''''+spec_sc.code, 1,5) in (SELECT service_code from #ServiceCodeList)
OR substring(''''88''''+lab_sc.code, 1,5) in (SELECT service_code from #ServiceCodeList)
'') '
Is that just not allowed?
A #temp table is visible only to the current running session. Even openrowset() to the local server will not see the #temp table because openrowset executes on a different connection(& another session).
Or is there some different way I need to do it?
You could populate a staging table on the remote server and use that staging table pretending/as if it was a #table. Generic staging tables (with an int column and a varchar column) for passing through values work pretty well. In order to differentiate between parallel processes the staging table could have a sessionid column (which holds the ##spid on the local side).
For inspiration....
--local temp table
create table #localtemp
(
srvcode varchar(100)
);
insert into #localtemp(srvcode)
select ##servername + '-' + name
from sys.objects;
--populate a proxy table on the remote server
declare #foo int;
select #foo = objectid
from openrowset('SQLNCLI', 'Server=Universe;Trusted_Connection=yes',
'set fmtonly off;
if object_id(''tempdb.dbo.proxyservicetable'') is null
begin
create table tempdb.dbo.proxyservicetable
(
sessionid smallint,
servicecode varchar(100)
);
create clustered index uclxproxyservicetable on tempdb.dbo.proxyservicetable(sessionid);
end
select object_id(''tempdb.dbo.proxyservicetable'') as objectid;
');
select #foo as stagingtableobjectid --on the remoteservers
--cleanup any staging leftovers from previous executions
delete from
openrowset('SQLNCLI', 'Server=Universe;Trusted_Connection=yes',
'
select *
from tempdb.dbo.proxyservicetable;
')
where sessionid = ##spid;
--populate staging with values from the current execution (#localtemp as source)
insert into
openrowset('SQLNCLI', 'Server=Universe;Trusted_Connection=yes',
'
select *
from tempdb.dbo.proxyservicetable;
')
select ##spid, srvcode
from #localtemp;
--passthrough query, at the remote server
declare #sql nvarchar(max)
select #sql = 'select *
from openrowset(''SQLNCLI'', ''Server=Universe;Trusted_Connection=yes'',
'' select ##servername,* from tempdb.dbo.proxyservicetable where sessionid =' + cast(##spid as varchar(10)) + '''
)';
exec(#sql);
go
drop table #localtemp
go
I want to get dynamic pivot result into temp table by which I can send it back to C# code using select query.
DECLARE #partymasterid bigint = 2;
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
SELECT
#cols = STUFF((SELECT DISTINCT '[' + CAST(dbo.InventoryProductMaster.Name AS nvarchar(max)) + '],'
FROM dbo.InventoryBiltyMaster
INNER JOIN dbo.InventoryPartyProductPriceMaster ON dbo.InventoryBiltyMaster.InventoryPartyProductPriceMasterID = dbo.InventoryPartyProductPriceMaster.ID
INNER JOIN dbo.InventoryProductMaster ON dbo.InventoryPartyProductPriceMaster.InventoryProductMasterID = dbo.InventoryProductMaster.ID
WHERE dbo.InventoryBiltyMaster.PartyMasterID = #partymasterid
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 0, '');
SELECT #query =
'
SELECT
*
FROM
(SELECT
Count(dbo.InventoryBiltyMaster.ID) AS BiltyCount,
--dbo.InventoryBiltyMaster.InventoryProductMasterID,
--dbo.InventoryBiltyMaster.VehicleMasterID,
dbo.InventoryProductMaster.Name,
dbo.VehicleMaster.VehicleNumber
FROM
dbo.InventoryBiltyMaster
INNER JOIN dbo.InventoryPartyProductPriceMaster ON dbo.InventoryBiltyMaster.InventoryPartyProductPriceMasterID = dbo.InventoryPartyProductPriceMaster.ID
INNER JOIN dbo.InventoryProductMaster ON dbo.InventoryPartyProductPriceMaster.InventoryProductMasterID = dbo.InventoryProductMaster.ID
INNER JOIN dbo.VehicleMaster ON dbo.InventoryBiltyMaster.VehicleMasterID = dbo.VehicleMaster.ID
WHERE
dbo.InventoryBiltyMaster.PartyMasterID = ' + CAST(#partymasterid as nvarchar(50)) + '
GROUP BY
dbo.InventoryBiltyMaster.InventoryProductMasterID,
dbo.InventoryProductMaster.Name,
dbo.InventoryBiltyMaster.VehicleMasterID,
dbo.VehicleMaster.VehicleNumber
)
AS S
PIVOT
(
MAX(BiltyCount)
FOR [Name] IN (' + LEFT(#cols, LEN(#cols)-1) + ')
)AS pvt';
EXEC SP_EXECUTESQL #query
Here may be result have 3 column or 5 columns or 10-15 columns. it depends on the result of query. and this result i want on temp table for use more this data.
I want to use it in a stored procedure to send back result into ASP.NET MVC 5 with Entity Framework
You can't store inside a # (temp table) without defining it in the parent scope when using sp_executesql. In your case you have a dynamic pivot and you don't know what and how many columns are going to be there.
sp_executesql runs in a different session (sp_executesql creates its own session) and temp tables are session specific.
For your scenario you can use ## (global temp table). You can change your query like following.
SELECT #query =
'
SELECT
* into ##temp
FROM
(SELECT
Count(dbo.InventoryBiltyMaster.ID) AS BiltyCount,
--dbo.InventoryBiltyMaster.InventoryProductMasterID,
--dbo.InventoryBiltyMaster.VehicleMasterID,
dbo.InventoryProductMaster.Name,
dbo.VehicleMaster.VehicleNumber
FROM
dbo.InventoryBiltyMaster
INNER JOIN dbo.InventoryPartyProductPriceMaster ON dbo.InventoryBiltyMaster.InventoryPartyProductPriceMasterID = dbo.InventoryPartyProductPriceMaster.ID
INNER JOIN dbo.InventoryProductMaster ON dbo.InventoryPartyProductPriceMaster.InventoryProductMasterID = dbo.InventoryProductMaster.ID
INNER JOIN dbo.VehicleMaster ON dbo.InventoryBiltyMaster.VehicleMasterID = dbo.VehicleMaster.ID
WHERE
dbo.InventoryBiltyMaster.PartyMasterID = ' + CAST(#partymasterid as nvarchar(50)) + '
GROUP BY
dbo.InventoryBiltyMaster.InventoryProductMasterID,
dbo.InventoryProductMaster.Name,
dbo.InventoryBiltyMaster.VehicleMasterID,
dbo.VehicleMaster.VehicleNumber
)
AS S
PIVOT
(
MAX(BiltyCount)
FOR [Name] IN (' + LEFT(#cols, LEN(#cols)-1) + ')
)AS pvt';
EXEC SP_EXECUTESQL #query
--now you can use ##temp
Note: use of global temp table can lead to unpredictable behavior if it gets updated from multiple sessions, you may think of giving a unique name for each session.
Can someone remove the #sql variable and Replace function for #ClientID & directly write the Select query inside the stored procedure?
I'm getting some conversion error.
This is the stored procedure:
ALTER PROCEDURE GetToDoDetails
#ClientID VARCHAR(MAX) = NULL
AS
BEGIN
DECLARE #Sql VARCHAR(MAX)
SET #ClientID = REPLACE(#ClientID, ',', ''',''')
SET #Sql = 'SELECT
C.ClientID,
C.ClientToDoID,
C.AssignedToID,
C.ToBeCompletedBy,
C.ToDoTypeID,
S.UDDescription,
(CL.LastName +'',''+ CL.FirstName) "Client Name",
"CountRow"
FROM c_ToDo C
INNER JOIN s_UserDefinedOptions S ON C.ToDoTypeID = S.UDID
INNER JOIN c_Client CL ON C.ClientID = CL.ClientID
INNER JOIN (Select count(*) CountRow,ClientID from c_ToDo WHERE ClientID IN (''' + #ClientID+''') GROUP BY ClientID) CO
on C.ClientID = CO.ClientID
WHERE
C.ClientID IN (''' + #ClientID + ''')
ORDER BY C.ClientID ASC'
PRINT (#Sql)
EXEC (#Sql)
END
Thanks.
Currently i have to create a list of all tables & schemes used in several (terribly) long SQL statements.
Of course i could go manually through the SQL statements and write down every table used in a JOIN, Subselect and so on.
But i'd be curious if there is any easier way to do it. maybe with a "simple" SQL statement, but i have no clue how to do it.
Why am i asking? I'd have to do this for about 50 SQL-Unload-Statements, where all of them use between 30 and 70 tables. I guess you can imagine this quite boring work
Thanks in advance for any input & hints!
Have a read here: How can i get the list of tables in the stored procedure
;WITH stored_procedures AS (
SELECT
o.name AS proc_name, oo.name AS table_name,
ROW_NUMBER() OVER(partition by o.name,oo.name ORDER BY o.name,oo.name) AS row
FROM sysdepends d
INNER JOIN sysobjects o ON o.id=d.id
INNER JOIN sysobjects oo ON oo.id=d.depid
WHERE o.xtype = 'P')
SELECT proc_name, table_name FROM stored_procedures
WHERE row = 1
ORDER BY proc_name,table_name
All credit to the OP of the code above in the thread above.
I will post this answer & mark it as solved, as it was the best and easiest way to solve my issue.
Hint was in a comment by #whereisSQL (thanks a lot!)
Seems like it was already asked in this thread, but i didn't find it:
https://dba.stackexchange.com/questions/121346/need-to-get-identify-all-tables-and-columns-in-a-sql-query/121355#121355
There is an online "get table column" tool to list the tables of a SQL statement here:
http://107.170.101.241:8080/getTableColumn/
It lists all tables and additionally columns used in the statement! Perfect solution in my case.
This may help. Using DMVs
IF EXISTS (
SELECT *
FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[sp_dependsobj]')
AND type IN (
N'P'
,N'PC'
)
)
DROP PROCEDURE [dbo].[sp_dependsobj]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[sp_dependsobj] (#Objname NVARCHAR(2000))
AS
BEGIN
SET NOCOUNT ON
DECLARE #ObjID INTEGER
DECLARE #ObjType VARCHAR(100)
DECLARE #RowCount INTEGER = 0
SELECT #ObjID = OBJECT_ID(#Objname)
SELECT #ObjType = TYPE
FROM sys.objects
WHERE object_id = #ObjID
DECLARE #RefObjects AS TABLE (
[Object_name] SYSNAME
,[Type] VARCHAR(255)
,referenced_entity_name SYSNAME
,Column_name SYSNAME NULL
,referenced_entity_type VARCHAR(255)
)
DECLARE #FoundIn AS TABLE (
[Object_name] SYSNAME
,[Found In] SYSNAME
,[Type] VARCHAR(255)
)
DECLARE #SQLStr VARCHAR(MAX)
SET #SQLStr = '
WITH CTE_Objects
AS
(
SELECT o.NAME referencing_entity_name
,o.type_desc referencing_object_type
,sed.referenced_entity_name
,refobj.referenced_minor_name
,obj.type_desc
,obj.object_id
FROM sys.sql_expression_dependencies sed
INNER JOIN sys.objects o ON sed.referencing_id = o.[object_id]
INNER JOIN sys.objects obj ON sed.referenced_entity_name = obj.name
INNER JOIN sys.dm_sql_referenced_entities(''dbo.' + #Objname + ''', ''OBJECT'') refobj ON
refobj.referenced_id = sed.referenced_id
WHERE o.NAME = ''' + #Objname + '''
UNION
SELECT ''' + #Objname + ''', (SELECT type_desc from sys.objects where name = ''' + #Objname + '''), referenced_entity_name, NULL, obj.type_desc
,obj.object_id
FROM sys.dm_sql_referenced_entities (''dbo.' + '' + #Objname +
''', ''OBJECT'') refObj INNER JOIN sys.objects obj
ON refObj.referenced_entity_name = obj.name
)
SELECT CTE.referencing_entity_name Object_name, CTE.referencing_object_type Type, CTE.referenced_entity_name, CTE.referenced_minor_name Column_name, CTE.type_desc referenced_entity_type
FROM CTE_Objects CTE LEFT JOIN sys.columns cl ON
cte.object_id = cl.object_id AND cte.referenced_minor_name = cl.name
Order by cte.referencing_entity_name, CASE WHEN cte.referencing_entity_name IS NULL THEN 0 ELSE cl.column_id END
'
INSERT INTO #RefObjects (
[Object_name]
,[Type]
,referenced_entity_name
,Column_name
,referenced_entity_type
)
EXEC (#SQLStr)
SET #RowCount = ##ROWCOUNT
IF #RowCount > 0
BEGIN
SELECT *
FROM #RefObjects
END
SET #SQLStr = '
SELECT DISTINCT [Object Name] = o.NAME
,[Found In] = sp.NAME
,sp.type_desc Type
FROM sys.objects o
INNER JOIN sys.sql_expression_dependencies sd ON o.object_id = sd.referenced_id
INNER JOIN sys.objects sp ON sd.referencing_id = sp.object_id
WHERE o.NAME = ''' + #Objname + '''
UNION
SELECT ''' + #Objname + ''', referencing_entity_name, (SELECT type_desc from sys.objects where name = referencing_entity_name)
FROM sys.dm_sql_referencing_entities (''dbo.' + '' + #Objname + ''', ''OBJECT'');
'
INSERT INTO #FoundIn (
[Object_name]
,[Found In]
,[Type]
)
EXEC (#SQLStr)
SET #RowCount = ##ROWCOUNT
IF #RowCount > 0
BEGIN
SELECT *
FROM #FoundIn
END
IF NOT EXISTS (
SELECT 1
FROM #RefObjects
)
AND NOT EXISTS (
SELECT 1
FROM #FoundIn
)
BEGIN
PRINT 'Object does not reference any object, and no objects reference it.'
END
SET NOCOUNT OFF
END
GO
The question is old, but here is a regex for those that want the tables used in SQL statement (From/join).
I created it specially for my case, but it may be adapt and useful for others:
(from|join)[ ]{1,}[^.\n\ ]+?(?=(.))[.].*?(?=( |\n))
I have a stored procedure that dynamically creates a pivot query.
The procedure works and returns the correct data.
Now I have a requirement to show this data in a reporting system that can only pull from a table or view. Since you can not create a dynamic query in a view I tried to do a select from using openquery.
Example '
Select * from OpenQuery([MyServername], 'Exec
Instance.Schema.StoredProcedure')
I get the error back
"the linked server indicates the object has no columns".
I assume this is because of the first select statement that is stuffing the variable with column names.
CODE FROM PROCEDURE
ALTER PROCEDURE [dbo].[FuelCombustorMatrix]
AS
BEGIN
-- Use Master
SET NOCOUNT ON
Declare #cols nvarchar(2000),
#Tcols nvarchar(2000),
#Sql nvarchar (max)
select #cols = stuff ((
Select distinct '], ['+ModelName + ' ' + CombustorName
from CombustorFuel cf
join Model m on cf.modelid = m.modelid
join Combustors cb on cf.CombustorID = cb.CombustorID
where cf.CombustorID > 0
for XML Path('')
),1,2,'')+']'
Set #Tcols = replace(#Cols, ']', '] int')
--Print #Tcols
--Print #Cols
Set #Sql = 'Select GasLiquid, FuelType, '+ #Cols +'
from
(
Select GasLiquid, FuelType, ModelName+ '' '' +CombustorName ModelCombustor, CombFuelStatus+''- ''+CombFuelNote CombFuelStatusNote
from Frames f
join Family fa on f.Frameid = fa.frameid
join Model m on fa.FamilyID = m.FamilyID
join CombustorFuel cf on m.Modelid = cf.modelid
Join Combustors c on cf.CombustorId = c.CombustorID
join FuelTypes ft on cf.FuelTypeID = ft.FuelTypeID
where cf.CombustorFuelID > 0
and CombustorName <> ''''
) up
Pivot
(max(CombFuelStatusNote) for ModelCombustor in ('+ #Cols +')) as pvt
order by FuelType'
exec (#Sql)
END