Create View with option (maxrecursion)? - sql

I get a "Incorrect syntax near keyword 'OPTION'" error when attempting to save a view in SQL Server 2012. I'm trying to add a MAXRECURSION option to the end of my common table expression. All the examples I've seen and working CTEs I've created before don't mind the "OPTION (MAXRECURSION 0)" at the end of the CTE.
Anyone see why I'm getting this error? The CTE works without the OPTION clause, although it reaches the maximum number of recursions (100).
WITH CTE1 AS (select
t.[ID_DIM_PRODUIT]
,t.[ID_FACTURE]
,t.[date_facture]
,t.[quantite]
,t.[quantite_enstock]
from [dbo].[Fact_Stock] t
cross apply (select sum([quantite_enstock]) as stockTotal
from [dbo].[Fact_Stock]
where [ID_DIM_PRODUIT] = t.[ID_DIM_PRODUIT]
and [date_facture] <= t.[date_facture]
) as st),
DateList (ID_FACTURE,ID_DIM_PRODUIT,stockTotal,date_facture,stockDateEnd )
AS (
SELECT ID_FACTURE,ID_DIM_PRODUIT,stockTotal,date_facture
, LEAD(date_facture, 1) OVER ( PARTITION BY ID_DIM_PRODUIT ORDER BY date_facture ) AS stockDateEnd
FROM CTE1
UNION ALL
SELECT ID_FACTURE,ID_DIM_PRODUIT,mvtStock,stockTotal,DATEADD(DAY,1,date_facture),stockDateEnd FROM DateList
WHERE DATEADD(DAY,1,date_facture) < stockDateEnd
)
SELECT d.id_facture,d.ID_DIM_PRODUIT,d.stockTotal ,d.date_facture as date_stock ,t.[ID_FACT_STOCK]
,t.[id_dim_fournisseur]
,t.[id_dim_depot]
,t.[poidnetGlobal]
,t.[poidsBrutGloba]
,t.[quantite]
,t.[montant_HT]
,t.[montant_TTC]
,t.[cout_moyen_unitaire]
,t.[mvtStock]
,t.[prix_vente_unitaire]
,t.[prix_achat]
FROM DateList d , CTE1 t where
d.id_facture=t.ID_FACTURE and d.ID_DIM_PRODUIT=t.ID_DIM_PRODUIT ORDER BY ID_DIM_PRODUIT,date_stock
option (maxrecursion 0)

Related

Oracle SQL use subquery value in another subquery

Before I go any further please mind that I am not well experienced with SQL.
I have one query that is getting a single value (netto value) such as:
WITH cte_value_net AS (
SELECT
nvl(min(value_net),0) as value_net
FROM (
SELECT
i.serial as serial,
nvl(lag(i.value_net) OVER (PARTITION BY i.serial ORDER BY i.month), i.value_net) as value_net
FROM
inventory i
WHERE
i.ctypde IN (
SELECT
ctypde
FROM
appar ap
WHERE
ap.serial = in_serial -- this is the variable I want to set
)
AND
i.month IN (to_char(add_months(sysdate, -1), 'YYYYMM'), to_char(add_months(sysdate, -2), 'YYYYMM'))
AND
i.serial = in_serial -- this is the variable I want to set
) vn
GROUP BY vn.serial
)
In here I have to feed in the variable in_serial that I thought I could get from another subquery such as:
SELECT
(SELECT * FROM cte_value_net) AS value_net
FROM (
SELECT
lap.serial AS in_serial
FROM
applap lap
)
but I can not wrap my head around it why this in_serial is not visible to my custom CTE. Could someone explain me how can I propagate the value from subquery like this?
The error I am obviously getting is:
SQL Error [904] [42000]: ORA-00904: "IN_SERIAL"
Unfortunately I do not have any sample data. What I want to achieve is that I could feed in the returned in_serial from main subquery to my CTE.
Before I can get value_net I need my main query to return the in_serial, otherwise I do not have access to that value.
The trick I use is to produce an extra CTE that I usually call params that includes a single row with all computed parameters. Then, it's a matter of performing a CROSS JOIN with this CTE in any other CTE, subquery or main query, as needed.
For example:
with
params as ( -- 1. Create a CTE that returns a single row
select serial as in_serial from applap
),
cte_value_net AS (
select ...
from inventory i
cross join params -- 2. cross join against the CTE anywhere you need it
where ...
and i.serial = params.in_serial -- 3. Use the parameter
)
select ...
First, your second query is not syntactically correct, as there's a ',' before the FROM. You can write your query like this:
WITH cte_value_net AS (
SELECT
serial, nvl(min(value_net),0) as value_net
FROM (
SELECT
i.serial as serial,
nvl(lag(i.value_net) OVER (PARTITION BY i.serial ORDER BY i.month), i.value_net) as value_net
FROM
inventory i
WHERE
i.ctypde IN (
SELECT
ctypde
FROM
appar ap
WHERE
ap.serial = i.serial
)
AND
i.month IN (to_char(add_months(sysdate, -1), 'YYYYMM'), to_char(add_months(sysdate, -2), 'YYYYMM'))
) vn
GROUP BY vn.serial
)
select ...
from cte_value_net s join applap lap on (lap.serial=s.serial)
(adjust query to your schema ....)

Incorrect Syntax Near Keyword 'OPTION' in CTE Statement

I get a "Incorrect syntax near keyword 'OPTION'" error when attempting to save a view in SQL Server 2008. I'm trying to add a MAXRECURSION option to the end of my common table expression. All the examples I've seen and working CTEs I've created before don't mind the "OPTION (MAXRECURSION 0)" at the end of the CTE.
Anyone see why I'm getting this error? The CTE works without the OPTION clause, although it reaches the maximum number of recursions (100).
WITH CTE AS
(
SELECT
CDay,
InvAcct,
BuyerCode,
PartNumber,
ROP,
ROP_ROQ,
DailyDemand,
StartingInvQty,
SchedDeliveryQty,
CAST(StartingInvQty - DailyDemand/2.0 AS decimal(18,4)) AS ProjInvQty
FROM
qryInventorySimulation
WHERE
MBC = 'B' AND
CDay = CAST(CAST(GETDATE()AS date) as datetime)
UNION ALL
SELECT
qryInventorySimulation.CDay,
qryInventorySimulation.InvAcct,
qryInventorySimulation.BuyerCode,
qryInventorySimulation.PartNumber,
qryInventorySimulation.ROP,
qryInventorySimulation.ROP_ROQ,
qryInventorySimulation.DailyDemand,
qryInventorySimulation.StartingInvQty,
qryInventorySimulation.SchedDeliveryQty,
CAST(CTE.ProjInvQty + qryInventorySimulation.SchedDeliveryQty - qryInventorySimulation.DailyDemand AS decimal(18,4)) AS ProjInvQty
FROM
qryInventorySimulation INNER JOIN CTE ON qryInventorySimulation.InvAcct = CTE.InvAcct AND qryInventorySimulation.PartNumber = CTE.PartNumber AND qryInventorySimulation.CDay = DATEADD(d,1,CTE.CDay)
WHERE
qryInventorySimulation.CDay <= DATEADD(d,120,GETDATE())
)
SELECT * FROM CTE
OPTION (MAXRECURSION 0);
You can't apply this option within the view. You would need to apply it to the query calling the view. e.g.
CREATE VIEW dbo.V
AS
WITH CTE AS
( SELECT 1 AS A
UNION ALL
SELECT A + 1
FROM CTE
WHERE A < 50
)
SELECT *
FROM CTE;
GO
SELECT *
FROM dbo.V
OPTION (MAXRECURSION 0);
If you think of a view more as a stored subquery than a stored query (yes it can be called on it's own but it is not necessarily), and remember that its definition is expanded out into the main query (unless you are using NOEXPAND - which you could not on a view that contained a recursive CTE anyway), so in essence you are trying to do something like this:
WITH RecursiveCTE AS (...)
SELECT *
FROM T
INNER JOIN
( SELECT *
FROM RecursiveCTE
OPTION (MAXRECURSION 0)
) c
ON c.SomeField = T.SomeField;
Whereas the correct syntax would be:
WITH RecursiveCTE AS (...)
SELECT *
FROM T
INNER JOIN
( SELECT *
FROM RecursiveCTE
) c
ON c.SomeField = T.SomeField;
OPTION (MAXRECURSION 0)

SQL Server : connect by level for DATE type with union

I've found basic answer for replacing the Oracle's "CONNECT BY LEVEL" in this question but my case is little bit more complicated:
Basically things that I want to replace looks like this:
...
UNION ALL
Select
adate, 'ROAD' as TSERV_ID, 0 AS EQ_NBR
from
(SELECT
to_date(sysdate - 732,'dd/mm/yy') + rownum -1 as adate, rownum
FROM
(select rownum
from dual
connect by level <= 732)
WHERE rownum <= 732)
UNION ALL
Select
adate, 'PORTPACK' as TSERV_ID, 0 AS EQ_NBR
from
(SELECT
to_date(sysdate - 732,'dd/mm/yy') + rownum -1 as adate, rownum
FROM
(select rownum from dual connect by level <= 732)
WHERE rownum <= 732)
UNION ALL
....
Now, the single dual connect is easy, even if this is apparently not very efficient method
WITH CTE AS (
SELECT dateadd(day,-720,CONVERT (date, GETDATE())) as Datelist
UNION ALL
SELECT dateadd(day,1,Datelist)
FROM CTE
WHERE datelist < getdate() )
SELECT *,'ROAD' as Tserv_ID , 0 as EQ_NBR FROM CTE
option (maxrecursion 0)
repeating the union is hard because I get an error:
Incorrect syntax near the keyword 'with'. If this statement is a common table expression, an xmlnamespaces clause or a change tracking context clause, the previous statement must be terminated with a semicolon.
There are more parts of this union that I've provided here; I've tried to use the "WITH" only at start but no luck. Am I missing something obvious here?
EDIT: There is of course big question WHY I am even trying to do such thing: Personally, I wouldn't, but at the other end of the query there is a huge Crystal Report that runs once every month and which accepts data in this particular format. End of the FULL query's output is something like
3 columns of data
3 Columns of data
...
Currentdate-732,"ROAD",0
Currentdate -731,"ROAD",0
...
Currentdate, "ROAD,"0"
Currentdate -732, "PORTPAK", 0
Currentdate -731, "PORTPAK", 0
etc.
Are you trying to do:
WITH CTE1 AS (...),
CTE2 AS (...)
SELECT stuff FROM CTE1
UNION ALL
SELECT stuff FROM CTE2;
? This is a common challenge, I guess it is not very discoverable that in order to use more than one CTE, you just separate them with a comma.
That all said, it seems like you are just trying to generate a series of dates. A recursive CTE (never mind a series of many of them) is not the most efficient way to do this. Instead of telling us you want to replace CONNECT BY LEVEL and showing us the syntax you've tried, why don't you just show or describe the output you want? We've already got an appreciation that you've tried something on your own (thanks!) but we'd rather give you an efficient solution than bridging the gap to an inefficient one.
As an example, here is something that requires a lot less redundant code, and ( think gives you what you're after:
DECLARE #n INT = 722, #d DATE = CURRENT_TIMESTAMP;
;WITH v AS (SELECT v FROM (VALUES('ROAD'),('PORTPACK')) AS v(v)),
n AS (SELECT TOP (#n) n = ROW_NUMBER() OVER (ORDER BY number)
FROM master.dbo.spt_values ORDER BY n)
SELECT Datelist = DATEADD(DAY, 2-n.n, #d), Tserv_ID = v.v, EQ_NBR = 0
FROM n CROSS JOIN v
ORDER BY Tserv_ID, Datelist;

Sql Server select and update X rows

I'm using Sql Server 2012.
I need to select rows from a table for processing. The number of rows needs to be variable. I need to update the rows I'm selecting to a "being processed" status - I have a guid to populate for this purpose.
I've encountered several examples of using row_number() and a couple of examples of ways of using CTE's, but I'm not sure on how to combine them (or if that's even the correct strategy). I would appreciate any insight.
Here is what I have so far:
DECLARE #SessionGuid uniqueidentifier, #rowcount bigint
SELECT #rowcount = 1000
SELECT #sessionguid = newid()
DECLARE #myProductChanges table (
ProductChangeId bigint
, ProductTypeId smallint
, SourceSystemId tinyint
, ChangeTypeId tinyint );
WITH NextPage AS
(
SELECT
ProductChangeId, ServiceSessionGuid,
ROW_NUMBER() OVER (ORDER BY ProductChangeId) AS 'RowNum'
FROM dbo.ProductChange
WHERE 'RowNum' < #rowcount
)
UPDATE dbo.ProductChange
SET ServiceSessionGuid = #sessionguid, ProcessingStateId = 2, UpdatedDate = getdate()
OUTPUT
INSERTED.ProductChangeId,
INSERTED.ProductTypeId,
INSERTED.SourceSystemId,
INSERTED.ChangeTypeId
INTO #myProductChanges
FROM dbo.ProductChange as pc join NextPage on pc.ProductChangeId = NextPage.ProductChangeId
From here I will select from my temp table and return the data:
SELECT mpc.ProductChangeId
, pt.ProductName as ProductType
, ss.Name as SourceSystem
, ct.ChangeDescription as ChangeType
FROM #myProductChanges as mpc
join dbo.R_ProductType pt on mpc.ProductTypeId = pt.ProductTypeId
join dbo.R_SourceSystem ss on mpc.SourceSystemId = ss.SourceSystemId
join dbo.R_ChangeType ct on mpc.ChangeTypeId = ct.ChangeTypeId
ORDER BY ProductType asc
So far this doesn't work for me. I get an error when I try to run it:
Msg 8114, Level 16, State 5, Line 20
Error converting data type varchar to bigint.
I'm not clear on what I'm doing wrong - so - any help is appreciated.
Thanks!
BTW, here are some of the questions I've used as reference to try and solve this:
https://stackoverflow.com/questions/9777178
https://stackoverflow.com/questions/3319842
https://stackoverflow.com/questions/6402103
This subquery makes no sense:
SELECT
ProductChangeId, ServiceSessionGuid,
ROW_NUMBER() OVER (ORDER BY ProductChangeId) AS 'RowNum'
FROM dbo.ProductChange
WHERE 'RowNum' < #rowcount
You can't reference the alias RowNum at the same scope (and you are trying to compare a string, not an alias, anyway), because when the WHERE clause is parsed, the SELECT list hasn't been materialized yet. What you need is either another nest:
SELECT ProductChangeId, ServiceSessionGuid, RowNum
FROM (SELECT ProductChangeId, ServiceSessionGuid,
ROW_NUMBER() OVER (ORDER BY ProductChangeId) AS RowNum
FROM dbo.ProductChange
) AS x WHERE RowNum < #rowcount
Or:
SELECT TOP (#rowcount-1) ProductChangeId, ServiceSessionGuid,
ROW_NUMBER() OVER (ORDER BY ProductChangeId) AS RowNum
FROM dbo.ProductChange
ORDER BY ProductChangeId
Also please stop using 'alias' - when you need to delimit aliases (you don't in this case), use [square brackets].
I'm guessing, but I think you want <= rather than < if you want to affect #rowcount rows, not one less.
Another tip is that CTEs can be updated directly*, as shown here:
WITH NextPage AS
(
SELECT TOP(#rowcount) *
FROM dbo.ProductChange
)
UPDATE NextPage
SET ServiceSessionGuid = #sessionguid, ProcessingStateId = 2, UpdatedDate = getdate()
OUTPUT
INSERTED.ProductChangeId,
INSERTED.ProductTypeId,
INSERTED.SourceSystemId,
INSERTED.ChangeTypeId
INTO #myProductChanges
* The updates affect the base table in the CTE, i.e. dbo.ProductChange

Reuse subquery result in WHERE-Clause for INSERT

i am using Microsoft SQL Server 2008
i would like to save the result of a subquery to reuse it in a following subquery.
Is this possible?
What is best practice to do this? (I am very new to SQL)
My query looks like:
INSERT INTO [dbo].[TestTable]
(
[a]
,[b]
)
SELECT
(
SELECT TOP 1 MAT_WS_ID
FROM #TempTableX AS X_ALIAS
WHERE OUTERBASETABLE.LT_ALL_MATERIAL = X_ALIAS.MAT_RM_NAME
)
,(
SELECT TOP 1 MAT_WS_NAME
FROM #TempTableY AS Y_ALIAS
WHERE Y_ALIAS.MAT_WS_ID = MAT_WS_ID
--(
--SELECT TOP 1 MAT_WS_ID
--FROM #TempTableX AS X_ALIAS
--WHERE OUTERBASETABLE.LT_ALL_MATERIAL = X_ALIAS.MAT_RM_NAME
--)
)
FROM [dbo].[LASERTECHNO] AS OUTERBASETABLE
My question is:
Is this correct what i did.
I replaced the second SELECT Statement in the WHERE-Clause for [b] (which is commented out and exactly the same as for [a]), with the result of the first SELECT Statement of [a] (=MAT_WS_ID).
It seems to give the right results.
But i dont understand why!
I mean MAT_WS_ID is part of both temporary tables X_ALIAS and Y_ALIAS.
So in the SELECT statement for [b], in the scope of the [b]-select-query, MAT_WS_ID could only be known from the Y_ALIAS table. (Or am i wrong, i am more a C++, maybe the scope things in SQL and C++ are totally different)
I just wannt to know what is the best way in SQL Server to reuse an scalar select result.
Or should i just dont care and copy the select for every column and the sql server optimizes it by its own?
One approach would be outer apply:
SELECT mat.MAT_WS_ID
, (
SELECT TOP 1 MAT_WS_NAME
FROM #TempTableY AS Y_ALIAS
WHERE Y_ALIAS.MAT_WS_ID = mat.MAT_WS_ID
)
FROM [dbo].[LASERTECHNO] AS OUTERBASETABLE
OUTER APPLY
(
SELECT TOP 1 MAT_WS_ID
FROM #TempTableX AS X_ALIAS
WHERE OUTERBASETABLE.LT_ALL_MATERIAL = X_ALIAS.MAT_RM_NAME
) as mat
You could rank rows in #TempTableX and #TempTableY partitioning them by MAT_RM_NAME in the former and by MAT_WS_ID in the latter, then use normal joins with filtering by rownum = 1 in both tables (rownum being the column containing the ranking numbers in each of the two tables):
WITH x_ranked AS (
SELECT
*,
rownum = ROW_NUMBER() OVER (PARTITION BY MAT_RM_NAME ORDER BY (SELECT 1))
FROM #TempTableX
),
y_ranked AS (
SELECT
*,
rownum = ROW_NUMBER() OVER (PARTITION BY MAT_WS_ID ORDER BY (SELECT 1))
FROM #TempTableY
)
INSERT INTO dbo.TestTable (a, b)
SELECT
x.MAT_WS_ID,
y.MAT_WS_NAME
FROM dbo.LASERTECHNO t
LEFT JOIN x_ranked x ON t.LT_ALL_MATERIAL = x.MAT_RM_NAME AND x.rownum = 1
LEFT JOIN y_ranked y ON x.MAT_WS_ID = y.MAT_WS_ID AND y.rownum = 1
;
The ORDER BY (SELECT 1) bit is a trick to specify an indeterminate ordering, which, accordingly, would result in indeterminate rownum = 1 rows picked by the query. That is to more or less duplicate your TOP 1 without an explicit order, but I would recommend you to specify a more sensible ORDER BY clause to make the results more predictable.