How to execute a Stored Procedure on the results of a SQL Server CTE - sql

I have a CTE which returns DISTINCT ID's. I want to execute a scalar function on each of the Id's returned.
WITH cte (reqID) as
(SELECT DISTINCT pol.ReqID FROM
LOG_PackingListItems pli
JOIN
v_PO_LN pol on pol.PO_ID = pli.PoId
WHERE
pli.PackingListHeaderID = 1)
EXEC dbo.spUpdateLOG_ReqCompleteCheck reqID -- Error "Incorrect Syntax near EXEC"
The EXEC line is what I want to make work but I get a syntax error. Not sure if what I want to do is possible or if I do in fact have a syntax error. Any ideas?
EDIT:
I'm adding the code for the Stored Procedure since I am now using a Table-Valued Parameter as suggested by realnumber3012
EDIT:
I have changed my CTE code so it populates a Table-Type as realnumber has suggested. I now get an error when executing spUpdateLOG_ReqCompleteCheck "Subquery returns more than one value."
DECLARE #ReqIdTVP as ReqIdType;
DELETE FROM #ReqIDTVP;
with cte (reqID) as
(select distinct pol.ReqID from
LOG_PackingListItems pli
join
v_PO_LN pol on pol.PO_ID = pli.PoId
where
pli.PackingListHeaderID = #PackingListHeaderID)
INSERT INTO #ReqIdTVP
SELECT * FROM cte
EXEC dbo.spUpdateLOG_ReqCompleteCheck #ReqIdTVP
Sproc code :
Alter PROCEDURE spUpdateLOG_ReqCompleteCheck
(#ReqIdTVP ReqIdType READONLY )
AS
BEGIN
DECLARE #TotalOrd int
DECLARE #TotalRx int
DECLARE #ReqID char(8)
SET #ReqID = (SELECT ReqID FROM #ReqIdTVP)
SET #TotalOrd = (SELECT ISNULL(SUM(ORD_QTY),0)
FROM dbo.v_PoLnNonFreight l
WHERE l.ReqID = #reqID)
SET #TotalRx = (SELECT ISNULL(SUM(TotalRxSite),0)
FROM dbo.v_PoLnNonFreight l
WHERE l.ReqID = #reqID)
IF #TotalRx >= #TotalOrd
BEGIN
DECLARE #curDate datetime
SET #CurDate = ISNULL(#CurDate,GetDate())
SET NOCOUNT ON;
UPDATE LOG_ReqHeader
SET
ReqCompleteDate = #curDate,
ReqStatus = 'Complete'
WHERE ReqID = #ReqID
END
END

Seems that the only thing your stored proc does is to update a logging table: (it only changes state via this statement and doesn't return anything????
UPDATE LOG_ReqHeader
SET
ReqCompleteDate = #curDate,
ReqStatus = 'Complete'
WHERE ReqID = #ReqID
How about splitting the logic out and write a function (inline if possible that will evaluate the condition you are looking for (didn't really understand what you are doind there) -- run the function on the results of the CTE (wrapping it in another CTE if you want) with the CROSS APPLY OPERATOR.
You'd end up with a result set that looks like [ReqId], [UpdateLog] (where updateLog is a BIT)
Then simply do a set based upadete JOINING to the results:
UPDATE l SET
ReqCompleteDate = #curDate,
ReqStatus = 'Complete'
FROM
LOG_ReqHeader AS l
JOIN <CTE> AS c ON c.[ReqID] = l.[ReqID]
WHERE
c.[UpdateLog] = 0x1
Does this make any sense?

Related

How can i execute a stored procedure inside another stored procedure in SQL Server?

I´m working with SQLServer and I have two storedprocedures
The first one:
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON
DECLARE #FECHAFIN AS datetime;
SET #FECHAFIN = #FECHAINICIO + '23:59:59.999'
-- Insert statements for procedure here
SELECT
HC.idCorrida AS Referencia,
T.part AS Parte,
T.idTrabajo AS Trabajo,
HC.cantidadFabricado AS Piezas,
HC.cantidadRechazado AS Rechazo,
CONVERT(varchar(8), HC.tiempoProduccion) AS TiempoDeProduccion,
CONVERT(varchar(8), HC.tiempoMuerto) AS TiempoMuerto,
HC.fechaInicio AS Fecha,
U.nombreUsuario AS Usuario,
MA.nombre AS NombreMaquina,
TR.nombre AS NombreTripulacion,
TXM.idTurno AS Turno,
FROM [Produccion].[HistorialCorridas] AS HC
INNER JOIN [Produccion].[Trabajos] AS T
ON HC.idTrabajo = T.idTrabajo
INNER JOIN [Administracion].[Usuarios] AS U
ON U.idUsuario = HC.idUsuario AND U.idTripulacion = #IDTRIPULACION
INNER JOIN [Administracion].[Tripulaciones] AS TR
ON U.idTripulacion = TR.idTripulacion
WHERE HC.idTurnoXMaquina = #IDTURNOXMAQUINA
AND HC.fechaInicio >= #FECHAINICIO AND HC.fechaInicio <= #FECHAFIN
GROUP BY HC.idCorrida, T.part, T.idTrabajo, HC.cantidadFabricado, HC.cantidadRechazado,
HC.tiempoProduccion, HC.tiempoMuerto, HC.fechaInicio, U.nombreUsuario, MA.nombre, TR.nombre, TXM.idTurno
END
The second one:
BEGIN
SET NOCOUNT ON
SELECT
CONVERT(VARCHAR(8), DATEADD(SECOND,SUM(DATEDIFF(SECOND, '00:00:00', TiempoMuerto)),0),114) AS TotalTiempoMuerto,
P.etiqueta AS TipoParo
FROM [Produccion].[TiemposMuertos]
INNER JOIN [Administracion].[ParosXMaquina] AS PM
ON PM.id = idParoXMaquina
INNER JOIN [Administracion].[Paros] AS P
ON P.idParo = PM.idParo
WHERE idCorrida = #IDCORRIDA
GROUP BY P.etiqueta
END
The second one receives as a parameter a field called Referencia from the first stored procedure. What I want to do, is execute the second stored procedure from the first one, passing that parameter.
How could I achieve this? Is it possible to get the result in just one query?
Thanks in advance!!!
To call a PROCEDURE from another PROCEDURE that returns a value as output, you must use the out keyword for the output, which may be a table or other data type.
The following two procedures are defined.
In test1 procedure, the return parameter is specified with out.
In the second procedure, the first procedure is called as follows:
DECLARE #result int
EXEC test1 #result OUTPUT
CREATE PROCEDURE test1
(#outID int output)
AS
BEGIN
SET #outID = (select top 1 ID from table1)
END
CREATE PROCEDURE test2
AS
BEGIN
BEGIN
DECLARE #result int
EXEC test1 #result OUTPUT
SELECT ID from table2 where ID = #result
END;
END
GO
EXEC test2

HSQL SELECT Statement Not working

I am new in HSQL. Tried a Procedure like below;
CREATE PROCEDURE GetData(ObjectId VARCHAR(36)) READS SQL DATA
DYNAMIC RESULT SETS 1
BEGIN ATOMIC
DECLARE MaxVal NUMERIC(19,2);
DECLARE MinVal NUMERIC(19,2);
DECLARE BiggestObjectName VARCHAR(50);
DECLARE SmallestObjectName VARCHAR(50);
SET MaxVal = (SELECT MAX(HeightValue) FROM ObjectData WHERE ObjectId=ObjectId);
SET MinVal = (SELECT MIN(HeightValue) FROM ObjectData WHERE ObjectId=ObjectId);
SET BiggestObjectName = (SELECT ObjectName FROM ObjectData WHERE ObjectId=ObjectId AND HeightValue=MaxVal);
SET SmallestObjectName = (SELECT ObjectName FROM ObjectData WHERE ObjectId=ObjectId AND HeightValue=MinVal);
if MaxVal IS NOT NULL THEN
DECLARE result CURSOR WITH RETURN FOR
SELECT MaxVal AS MaximumHeight, MinVal AS MinimumHeight, BiggestObjectName AS LargestDisplayCaseName, SmallestObjectName AS SmallestDisplayCaseName FOR READ ONLY;
OPEN result;
END
But i get error;
Caused by: java.sql.SQLSyntaxErrorException: unexpected token: ; required: INTO
Is it not the correct syntax?
Any help is appreciated :)
There are several things wrong with your stored procedure. For one I don't think you can declare a cursor as part of an IF statement.
Assignment of a variable needs to be either using select ... into or you need to put the select statement between parentheses:
SET MaxVal = (SELECT MAX(HeightValue) FROM ObjectData WHERE ObjectId=ObjectId);
SET ObjectName = (SELECT ObjectName FROM ObjectData WHERE ObjectId=ObjectId AND HeightValue=MaxVal);
or
SELECT MAX(HeightValue)
into maxval
FROM ObjectData
WHERE ObjectId=ObjectId;
You also can't use = or <> to compare NULL values. if MaxVal != NULL THEN needs to be
if maxval is not null then
...
end if; --<< you also forgot the `end if`
You also can't use a SELECT statement without a FROM clause, and I don't think you can define a cursor that only selects values from variables in HSQLDB.
But don't need the intermediate selects anyway, you can do that in a single select:
CREATE PROCEDURE GetData(ObjectId VARCHAR(36)) READS SQL DATA
DYNAMIC RESULT SETS 1
BEGIN ATOMIC
DECLARE result CURSOR FOR
select o1.heightvalue as maximumheight, o1.objectname as displaycasename
from objectdata o1
where objectid = 'one'
and heightvalue = (select max(heightvalue)
from objectdata o2
where o2.objectid = o1.objectid);
OPEN result;
END;
You are not using the correct syntax to create the procedure. According to the syntax you have to put the value(s) selected from table into a local variable(s). Follow the following example.
CREATE PROCEDURE get_customer(IN id INT, OUT firstname VARCHAR(50), OUT lastname VARCHAR(50))
READS SQL DATA
BEGIN ATOMIC
-- this statement uses the id to get firstname and lastname
SELECT first_name, last_name INTO firstname, lastname FROM customers WHERE cust_id = id;
END
I hope it'll help you.

Can we create a view after a script from a variable?

I would like to create a view at the end of the following request.
I know that 'create view' must be the first statement in a query batch. My problem is that for this query i must use a variable (#listOfIDRUB).
This variable is only fill correctly at the end of my little script.
I also have tried to create the view before my first declaration but it created a problem with "DECLARE".
So is it possible to create a view easily from the result of my script or i have to do something else ?
DECLARE #CounterResId int;
DECLARE #lePath varchar(255);
DECLARE #listOfIDRUB TABLE (EXTERNALREFERENCE uniqueidentifier, ID varchar(255), DOCID varchar(255) );
DECLARE #Max int;
SET #lePath = '';
SET #CounterResId = 1;
SET #Max = (SELECT COUNT(*) FROM SYNTHETIC..EXTRANET_PURGE WHERE TYPE_SUPPR = 'ResId')
WHILE (#CounterResId <= #Max )
BEGIN;
set #lePath =
(select tmp.lePath from
(
select row_number() over(order by path)as NumLigne, CONCAT(path, '%' ) as lePath from RUBRIQUE
WHERE MODELE = 'CAEEE64D-2B00-44EF-AA11-6B72ABD9FE38'
and CODE in (SELECT ID FROM SYNTHETIC..EXTRANET_PURGE where TYPE_SUPPR='ResId')
) tmp
WHERE tmp.NumLigne = #CounterResId)
INSERT INTO #listOfIDRUB(EXTERNALREFERENCE, ID, DOCID)
SELECT SEC.EXTERNALREFERENCE , SEC.ID, SEC.DOCUMENTID
FROM WEBACCESS_FRONT..SECTIONS sec
inner join rubrique rub ON rub.ID_RUBRIQUE = sec.EXTERNALREFERENCE
inner join template_tree_item tti ON tti.id_template_tree_item = rub.modele
inner join template t ON t.id_template = tti.template
WHERE t.CODE IN (SELECT TEMPLATE_CODE from SYNTHETIC..EasyFlowEngineListTemplateCode)
and rub.path like #lePath
print #CounterResId;
print #lePath;
set #CounterResId = #CounterResId + 1;
END;
select * from #listOfIDRUB;
Instead of select * from #listOfIDRUB
i wanted create view test as select * from listOfIDRUB
I have also tried create view test as (all my request)
Whenever you ask something about SQL please state your RDBMS (product and version). The answers are highly depending on this...
From your code I assume this is SQL Server.
So to your question: No, a VIEW must be "inlineable" (single-statement or "ad-hoc") statement.
You might think about a multi-statement UDF, but this is in almost all cases a bad thing (bad performance). Only go this way, if your result table will consist of rather few rows!
Without knowing your tables this is rather blind walking, but you might try this (add parameters, if you can transfer external operations (e.g. filtering) into the function):
CREATE FUNCTION dbo.MyFunction()
RETURNS #listOfIDRUB TABLE (EXTERNALREFERENCE uniqueidentifier, ID varchar(255), DOCID varchar(255) )
AS
BEGIN
DECLARE #CounterResId int;
DECLARE #lePath varchar(255);
DECLARE #Max int;
SET #lePath = '';
SET #CounterResId = 1;
SET #Max = (SELECT COUNT(*) FROM SYNTHETIC..EXTRANET_PURGE WHERE TYPE_SUPPR = 'ResId')
WHILE (#CounterResId <= #Max )
BEGIN;
set #lePath =
(select tmp.lePath from
(
select row_number() over(order by path)as NumLigne, CONCAT(path, '%' ) as lePath from RUBRIQUE
WHERE MODELE = 'CAEEE64D-2B00-44EF-AA11-6B72ABD9FE38'
and CODE in (SELECT ID FROM SYNTHETIC..EXTRANET_PURGE where TYPE_SUPPR='ResId')
) tmp
WHERE tmp.NumLigne = #CounterResId)
INSERT INTO #listOfIDRUB(EXTERNALREFERENCE, ID, DOCID)
SELECT SEC.EXTERNALREFERENCE , SEC.ID, SEC.DOCUMENTID
FROM WEBACCESS_FRONT..SECTIONS sec
inner join rubrique rub ON rub.ID_RUBRIQUE = sec.EXTERNALREFERENCE
inner join template_tree_item tti ON tti.id_template_tree_item = rub.modele
inner join template t ON t.id_template = tti.template
WHERE t.CODE IN (SELECT TEMPLATE_CODE from SYNTHETIC..EasyFlowEngineListTemplateCode)
and rub.path like #lePath
--print #CounterResId;
--print #lePath;
set #CounterResId = #CounterResId + 1;
END;
RETURN;
END
You can call it like this (very similar to a VIEW)
SELECT * FROM dbo.MyFunction();
And you might even use it in joins...
And last but not least I'm quite sure, that one could solve this without declares and a loop too...

TSQL Cursor calls Stored Procedure 'View Nesting Level Exceeded'

Being a bit of a SQL Newb, I tried to create a SQL script to execute a stored procedure on a number of rows using a cursor. I found the code to create the cursor and the stored procedure works as expected.
However, sometimes during execution I get the error
Maximum stored procedure, function, trigger, or view nesting level exceeded (limit 32)
Here is the SQL script in question. Any ideas why I get this error?
CREATE PROCEDURE p_MigrateRenewalOptions
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #OrderId VARCHAR(255) = NULL
DECLARE #RenewalId INT = 0
DECLARE #DiscountCode VARCHAR(255) = NULL
DECLARE #UpgradeCode VARCHAR(255) = NULL
DECLARE #ProductCode VARCHAR(255) = NULL
DECLARE rCursor CURSOR FOR
SELECT RenewalId FROM t_Renewals WHERE DiscountCode IS NOT NULL AND UpgradeCode IS NOT NULL
OPEN rCursor
FETCH NEXT FROM rCursor INTO #RenewalId
-- Iterate over t_Renewals with DiscountCode, UpgradeCode
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #OrderId = OrderId from t_Renewals where RenewalId = #RenewalId
SELECT #DiscountCode = DiscountCode from t_Renewals where RenewalId = #RenewalId
SELECT #UpgradeCode = UpgradeCode from t_Renewals where RenewalId = #RenewalId
SELECT #ProductCode = ProductCode from t_Order Where OrderId = #OrderId
-- Create renewal options for the t_Renewal entry
EXEC p_SelectOrCreateRenewalOptions #OrderId
-- Migrate the DiscountCode, UpgradeCode from the renewal record
UPDATE t_Renewal_Option
SET CouponCode = #DiscountCode
WHERE RenewalId = #RenewalId AND OptionType = 0 AND CouponCode IS NULL
UPDATE t_Renewal_Option
SET CouponCode = #UpgradeCode
WHERE RenewalId = #RenewalId AND OptionType = 1 AND CouponCode IS NULL
-- NULL the Renewal record DiscountCode, UpgradeCode
UPDATE t_Renewals
SET DiscountCode = NULL, UpgradeCode = NULL
WHERE RenewalId = #RenewalId
FETCH NEXT FROM rCursor INTO #RenewalId
END
CLOSE rCursor
DEALLOCATE rCursor
END
GO
One thing I noted, that the statement
-- Create renewal options for the t_Renewal entry
EXEC p_SelectOrCreateRenewalOptions #OrderId
returns a set, e.g. if you run this code outside of a stored procedure, inside SQL Server Management Studio, you get several sets of results in the output window. Is this the cause? Is it possible to dump the data as I don't need the returned rows from p_SelectOrCreateRenewalOptions
EDIT: As requested, here is the code for p_SelectOrCreateRenewalOptions
CREATE PROCEDURE p_SelectOrCreateRenewalOptions
(
#OrderId VARCHAR(255) = NULL
)
AS
BEGIN
SET NOCOUNT ON
DECLARE #ProductCode VARCHAR(255);
SELECT #ProductCode = ProductCode FROM t_Order WHERE OrderId = #OrderId;
DECLARE #StaticOptionCount INT;
SET #StaticOptionCount = dbo.f_QueryRenewalOptionCount(#ProductCode);
DECLARE #OptionCount INT;
SELECT #OptionCount = COUNT(*) FROM t_Renewal_Option ro INNER JOIN t_Renewals r ON r.RenewalId = ro.RenewalId WHERE r.OrderId = #OrderId
IF (#OptionCount != #StaticOptionCount)
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
SELECT #OptionCount = COUNT(*) FROM t_Renewal_Option ro INNER JOIN t_Renewals r ON r.RenewalId = ro.RenewalId WHERE r.OrderId = #OrderId
IF (#OptionCount != #StaticOptionCount)
BEGIN
DECLARE #RenewalId INT;
SELECT #RenewalId = RenewalId FROM t_Renewals r WHERE r.OrderId = #OrderId;
DELETE FROM t_Renewal_Option WHERE RenewalId = #RenewalId;
INSERT INTO t_Renewal_Option (RenewalId, OptionProductCode, OptionType, LastUpdated)
SELECT #RenewalId, sro.OptionProductCode, sro.OptionTypeId, GETDATE()
FROM t_Static_Renewal_Option sro
WHERE sro.ProductCode = #ProductCode
END
COMMIT TRANSACTION
END
SELECT ro.* FROM t_Renewal_Option ro INNER JOIN t_Renewals r ON r.RenewalId = ro.RenewalId WHERE r.OrderId = #OrderId
END
GO
f_QueryRenewalOptionCount simply does a select:
-- f_QueryRenewalOptionCount
SELECT #Count = COUNT(*) FROM t_Static_Renewal_Option o WHERE o.ProductCode = #ProductCode
Triggers wise, I don't think we have any triggers. There is a trigger on the t_Order table on INSERT but apart from that, nothing else.
UPDATE 12-SEP-14:
This 'works' but I don't understand the problem fully. I wrapped the EXEC call into a Begin/End Transaction
Note this script is called once to migrate some old part of the schema to a new part. Its not performance intensive and just has to 'work once'
Do you have a trigger on any of your tables? If an update trigger updates its own table, it will trigger itself again, which will update the table again, etc.
Do you use view or function anywhere in your code? Views can call on other views or functions, which call other views / functions.
The cursor here, while makes my skin crawl, is likely not the cause of the error your seeing. Without being able to see what the nested stored procedure is doing, this is just an educated guess, but that's probably where the issue lies.
The error you're seeing happens whenever a recursive SQL operation basically gets stuck in an infinite loop. The two ways I have had it happen is with a poorly written recursive CTE (basically a table which infinitely unions to itself) or when, as is more likely in this case, a store procedure calls a stored procedure which calls a stored procedure... and so on down the line. For instance (and I haven't tested this) if p_selectOrCreateRenewalOptions called p_MigrateRenewalOptions, you'd probably see a similar error.

Creating a Stored Procedure

I am trying to create a stored procedure that will allow me to update the ActualArrivalDate and ActualDepartureDate in a TripStop table where StopTypeCode = Drop in the TripStopOrder table.
Create Proc spUpdateTable
#OrderID int, #ArrivalDate datetime, #DepartureDate datetime
As
Begin
Begin Transaction
Select * From dbo.TripStopOrder
Join dbo.TripStop ON dbo.TripStopOrder.TripStopID = dbo.TripStop.TripStopID
Where OrderID = ''and StopTypeCode ='Drop'
Once I find this record I need to grab the TripStopId and pass it into the Update statement.
Not sure how to this...can I use a temp table then run another Select statement to pick up the TripStopID?
Update dbo.TripStop SET ArrivalDate='',DepartureDate=''
Where TripStopID = ''
End
Commit
Any ideas or suggestions would be greatly appreciated. ~Newbie~
You can assign the value to a variable such as #TripStopId:
DECLARE #TripStopId INT
Select #TripStopid = TripStopId
From dbo.TripStopOrder
Join dbo.TripStop ON dbo.TripStopOrder.TripStopID = dbo.TripStop.TripStopID
Where OrderID = ''and StopTypeCode ='Drop'
Then you can use it in your UPDATE statement.
Update dbo.TripStop SET ArrivalDate='',DepartureDate=''
Where TripStopID = #TripStopId
Instead of doing the SELECT and then the UPDATE, just change your WHERE clause in your UPDATE statement:
WHERE TripStopID = (SELECT T.TripStopID FROM TripStopOrder O
INNER JOIN TripStop T ON O.TripStopID = T.TripStopID
WHERE OrderID = #OrderID AND StopTypeCode = 'Drop')