How do I resolve the circular reference in this LAG statement? - sql-server

I need to use LAG to grab the last row's EndingUnits value and use it as the OpeningUnits for the current row. Part of the problem is that I can't use any of the aliases like EndingUnits or OpeningUnits in the select statement where they're defined. The other problem is the definition of EndingUnits relies on OpeningUnits which itself is dependent by EndingUnits.
The only table involved is DividendPricing which looks like this:
DividendPricingID PK, int
FiscalPeriod smalldatetime
DivPrice money
DivFactor float
-
OpeningUnits = LAG(EndingUnits, 1, 1) OVER (ORDER BY FiscalPeriod)
DRIP = (LAG(EndingUnits, 1, 1) OVER (ORDER BY FiscalPeriod))/DivPrice*DivFactor
EndingUnits = OpeningUnits + DRIP
My query looks like this, but obviously does not work:
SELECT FiscalPeriod,
LAG(EndingUnits, 1, 1) OVER (ORDER BY FiscalPeriod) AS OpeningUnits,
DivPrice,
DivFactor,
LAG(EndingUnits, 1, 1) OVER (ORDER BY FiscalPeriod)/DivPrice*DivFactor AS [DRIP],
LAG(EndingUnits, 1, 1) OVER (ORDER BY FiscalPeriod) + (LAG(EndingUnits, 1, 1) OVER (ORDER BY FiscalPeriod)/DivPrice*DivFactor) AS EndingUnits
FROM DividendPricing

I do not really understand the outcome you expect, however, to use the LAG() the way you intend, you have to pull it up from a starting point. I threw together this example using CTEs, perhaps it can help lead to an ideal solution in your case.
DECLARE #T TABLE(FiscalPeriod SMALLDATETIME,DivPrice MONEY,DivFactor FLOAT)
INSERT #T VALUES
('01/01/2012',2,1.2),('04/01/2012',4,1.23),('07/01/2012',6,1.1),('10/01/2012',8,1.15),
('01/01/2013',2,1.2),('04/01/2013',4,1.23),('07/01/2013',6,1.1),('10/01/2013',8,1.15)
;WITH Start AS
(
SELECT
EndingUnits = 1, FiscalPeriod,DivPrice, DivFactor
FROM
#T
)
,NormalizedEnding AS
(
SELECT
FiscalPeriod, DivPrice, DivFactor,
EndingUnits = LAG(EndingUnits, 1, 1) OVER (ORDER BY FiscalPeriod) + (LAG(EndingUnits, 1, 1) OVER (ORDER BY FiscalPeriod) / DivPrice * DivFactor)
FROM
Start
),
ContinuationCalulation AS
(
SELECT
FiscalPeriod,DivPrice,DivFactor,
OpeningUnits = LAG(EndingUnits, 1, 1) OVER (ORDER BY FiscalPeriod),
DRIP = (LAG(EndingUnits, 1, 1) OVER (ORDER BY FiscalPeriod))/DivPrice*DivFactor,
x=EndingUnits
FROM
NormalizedEnding
)
SELECT
FiscalPeriod,DivPrice,DivFactor,OpeningUnits,
EndingUnits = OpeningUnits + DRIP,
x
FROM
ContinuationCalulation

Related

My SQL query runs perfectly, but when I add the CTE function, I get an error

My SQL query runs perfectly, but when I add the CTE function, I get an error
Please check this code and let me know what's wrong with the CTE:
WITH Consumption_details(UnitId, consumption, monthof, yearof) AS
(
SELECT
UnitId, SUM(consumption) AS consumption,
monthof, yearof
FROM
(SELECT
UnitId, apartment_consumption,
DATEPART(MONTH, day) AS monthof,
DATEPART(YEAR,day) AS yearof
FROM
MeterReading) AS t
GROUP BY
yearof, monthof, UnitId
HAVING
monthof = 2 AND yearof = 2022
ORDER BY
UnitID
)
You can't have ORDER BY inside the CTE (unless you also include TOP, which you shouldn't do in this case), and you need to do something with the CTE - it's just an expression, not a query on its own.
;;;/*be safe!*/;;;With cd(SonnenUnitId, consumption, monthof, yearof) AS
(
SELECT SonnenUnitId, ...
...
GROUP BY yearof, monthof, SonnenUnitId
HAVING monthof =2 and yearof =2022
)
SELECT * FROM cd Order by SonnenUnitID;
As an aside, this query could be a whole lot more efficient with no need for a CTE and a subquery, any of the HAVING, and the scan potentially becoming a seek.
DECLARE #mo int = 2, #yo int = 2022;
DECLARE #m date = DATEFROMPARTS(#yo, #mo, 1);
SELECT SonnenUnitId,
SUM(apartment_consumption) AS consumption,
monthof = #mo,
yearof = #yo
FROM dbo.SonnenMeterReading
WHERE [day] >= #m
AND [day] < DATEADD(MONTH, 1, #m)
GROUP BY SonnenUnitId
ORDER BY SonnenUnitId;

How to use distinct keyword for single column when selecting multiple columns in a Select Statement in SQL Server

In SQL Server, I want to use distinct keyword for a single column [BLC.Container_No] in a SELECT statement which is having multiple columns.
My query:
SELECT BLD.VV_CODE,
V.Vessel_name,
BLD.Arrival_date,
ISNULL(IGM.VIR_NO, 'NULL') AS VIR_NO,
ISNULL(BLD.TERMINAL_CODE, 'NULL') AS TERMINAL_CODE,
BLD.BL_NO,
ISNULL(BLD.Parent_BL, 'NULL') AS Parent_BL,
BLD.Consignee_Description,
DO.DO_Issue_Date,
CA.CAgent_Name,
BLC.Container_No,
CS.Container_Size_Description
FROM Vessel V,
IGM,
BL_DATA BLD,
CAgent CA,
Delivery_Order DO,
BL_Container BLC,
Container_Size CS
WHERE V.Vessel_code = SUBSTRING(BLD.VV_CODE, 1, 3)
AND BLD.VV_CODE = IGM.VV_CODE
AND DO.CAgent_Code = CA.CAgent_Code
AND BLD.BL_NO = BLC.BL_NO
AND BLC.Container_Size_Code = CS.Container_Size_Code;
You can't do that. How would SQL know which BLC.Container_NO you want?You have to tell sql which one of those you want. You can use this one.
With cte as(
SELECT BLD.VV_CODE,
V.Vessel_name,
BLD.Arrival_date,
ISNULL(IGM.VIR_NO, 'NULL') AS VIR_NO,
ISNULL(BLD.TERMINAL_CODE, 'NULL') AS TERMINAL_CODE,
BLD.BL_NO,
ISNULL(BLD.Parent_BL, 'NULL') AS Parent_BL,
BLD.Consignee_Description,
DO.DO_Issue_Date,
CA.CAgent_Name,
BLC.Container_No,
ROW_NUMBER() OVER (PARTITION BY BLD.VV_CODE ORDER BY BLC.Container_No asc) AS row_num
,CS.Container_Size_Description
FROM Vessel V,
IGM,
BL_DATA BLD,
CAgent CA,
Delivery_Order DO,
BL_Container BLC,
Container_Size CS
WHERE V.Vessel_code = SUBSTRING(BLD.VV_CODE, 1, 3)
AND BLD.VV_CODE = IGM.VV_CODE
AND DO.CAgent_Code = CA.CAgent_Code
AND BLD.BL_NO = BLC.BL_NO
AND BLC.Container_Size_Code = CS.Container_Size_Code)
SELECT *
FROM cte
WHERE rn = 1
you just cant.. unless you group columns.. then you can specify grouping functions like count(distinct field1) or sum(distinct field2) etc.

Struggling with conditional logic

EDITED QUESTIONS:
I can't seem to figure out the conditional logic for my query.
I am certain that this is simple but I have been spinning my wheels on this one for too long - it is just one of those days.
Any help is always appreciated.
CURRENT QUERY:
SELECT
r.WidgetPK
,r.WidgetName
,r.WeightRateFlag [WeightRateFlag]
,r.Rate [Rate]
,r.Breakpoint [Breakpoint]
,MAX(ISNULL(f.ShippingFee,0)) [ShippingFee]
,MAX(ISNULL(f.OtherFee,0)) [OtherFee]
,MAX(r.weight) [Weight]
FROM
#Rates r
LEFT JOIN #Fees f ON f.WidgetPK = r.WidgetPK
I left out the GROUP BY for simplicity.
If the WeightRateFlag has a 1 in it in ANY row for each WidgetPK then all rows with a 0 will not be returned. If the WeightRateFlag has no rows with a 1 in it then ALL rows will be returned.
Sorry the original question wasn't clear - searches aren't helping and I asked a coworker. I think my problem may just be that I am asking the wrong question here and in my searches.
SELECT
r.WidgetPK
,r.WidgetName
,r.WeightRateFlag [WeightRateFlag]
,r.Rate [Rate]
,r.Breakpoint [Breakpoint]
,MAX(ISNULL(f.ShippingFee,0)) [ShippingFee]
,MAX(ISNULL(f.OtherFee,0)) [OtherFee]
,MAX(r.weight) [Weight]
FROM
#Rates r
LEFT JOIN #Fees f ON f.WidgetPK = r.WidgetPK
WHERE r.WeightRateFlag = 1
UNION ALL
SELECT
r.WidgetPK
,r.WidgetName
,r.WeightRateFlag [WeightRateFlag]
,r.Rate [Rate]
,r.Breakpoint [Breakpoint]
,MAX(ISNULL(f.ShippingFee,0)) [ShippingFee]
,MAX(ISNULL(f.OtherFee,0)) [OtherFee]
,MAX(r.weight) [Weight]
FROM
#Rates r
LEFT JOIN #Fees f ON f.WidgetPK = r.WidgetPK
WHERE r.WeightRateFlag = 0
AND NOT EXISTS (SELECT * FROM #rates r2 WHERE r2WeightRateFlag =1 AND r.WidgetName = r2.WidgetName)
A bit convoluted-looking, but CTEs can be extremely helpful. Plus they should work pretty well with the optimizer. Modify for the columns you need.
/* TEST DATA SETUP */
IF OBJECT_ID(N'tempdb..#t1') IS NOT NULL
BEGIN
DROP TABLE #t1
END
CREATE TABLE #t1 (WidgetPK int, col1 varchar(19), WeightRateFlag bit, ShippingFee money, OtherFee money, [Weight] int);
INSERT INTO #t1 (WidgetPK, col1, WeightRateFlag, ShippingFee, OtherFee, [Weight])
VALUES
(1, 'showme1', 1, 9, 1, 1)
, (1, 'noshow2', 0, 2, 9, 2)
, (1, 'noshow3', 0, 1, 2, 9)
, (2, 'showme1', 1, 9, 9, 9)
, (3, 'showme1', 0, 1, 9, 1)
, (3, 'showme2', 0, 9, 9, 9)
, (3, 'showme3', 0, 9, 1, 9)
;
/* QUERY STARTS HERE */
WITH cte1 AS (
SELECT x1.*
FROM (
SELECT #t1.WidgetPK, #t1.col1, #t1.WeightRateFlag, #t1.ShippingFee, #t1.OtherFee, #t1.[Weight]
, RANK() OVER (PARTITION BY #t1.WidgetPK ORDER BY #t1.WeightRateFlag DESC) AS rn
FROM #t1
) x1 WHERE x1.rn = 1
)
, cte2 AS (
SELECT cte1.WidgetPK
, MAX(cte1.ShippingFee) AS ShippingFee
, MAX(cte1.OtherFee) AS OtherFee
, MAX(cte1.[Weight]) AS [Weight]
FROM cte1
GROUP BY cte1.WidgetPK
)
SELECT cte1.WidgetPK, cte1.col1, cte1.WeightRateFlag, cte2.ShippingFee, cte2.OtherFee, cte2.[Weight]
FROM cte1
LEFT OUTER JOIN cte2 ON cte1.WidgetPK = cte2.WidgetPK
;

The latest two status of sql server agent jobs

I need to make a query that gets the the status of sql server agent jobs.
The tricky part, is that I both want the time the job last have failed (if any) AND when it last ran successful - is that possible?
So far I have only been able to get the last successful run or the last failed run of a job.
SELECT
job.name
,jobh.run_date
,DATEADD(HOUR, (jobh.run_time / 1000000) % 100,
DATEADD(MINUTE, (jobh.run_time / 10000) % 100,
DATEADD(SECOND, (jobh.run_time / 100) % 100,
DATEADD(MILLISECOND, (jobh.run_time % 100) * 10,
cast('00:00:00' as TIME(0)))))) AS 'tidspunkt'
,jobh.run_duration
FROM msdb.dbo.sysjobhistory jobh
JOIN [msdb].[dbo].[sysjobs] job ON jobh.job_id = job.job_id
WHERE jobh.step_id = 0
AND jobh.message LIKE '%failed%'
AND (jobh.run_date = CONVERT(VARCHAR, GETDATE(), 112) OR jobh.run_date = CONVERT(VARCHAR, DATEADD(DAY, -1, GETDATE()), 112) )
AND job.name = 'UpdateCPR'
ORDER BY jobh.run_date DESC, jobh.run_time DESC
Just change the line...
AND jobh.message LIKE '%failed%'
...to following...
AND (jobh.message LIKE '%failed%' OR jobh.message LIKE '%succeeded%')
You will get multiple rows. If you wish, you can group that by LIKE result and select TOP 1 from both to retrieve it in one row.
EDIT
Use this to get both fail and success info in one row.
;WITH jobRuns AS (
SELECT
job.job_id,
job.name,
jobh.run_time,
jobh.run_duration,
execStatus
FROM msdb.dbo.sysjobhistory jobh
JOIN msdb.dbo.sysjobs job
ON jobh.job_id = job.job_id
CROSS APPLY(
SELECT execStatus = CASE
WHEN jobh.message LIKE '%failed%' THEN 0
WHEN jobh.message LIKE '%succeeded%' THEN 1 END) ext
WHERE
jobh.step_id = 0
and job.name LIKE '%testjob%'
/*GROUP BY
job.job_id,
job.name,
jobh.run_time,
execStatus*/)
,lastRuns AS (
SELECT TOP 1
jobRuns.job_id,
jobRuns.name,
run_time_failed = failed.run_time,
run_duration_failed = failed.run_duration,
run_time_succeeded = success.run_time,
run_duration_succeeded = success.run_duration
FROM jobRuns
CROSS APPLY(
SELECT TOP 1 run_time, run_duration
FROM jobRuns
WHERE execStatus = 0
ORDER BY run_time DESC) failed
CROSS APPLY(
SELECT TOP 1 run_time, run_duration
FROM jobRuns
WHERE execStatus = 1
ORDER BY run_time DESC) success
)
SELECT *
FROM lastRuns

SQL server: WHERE in xml field

Example: I have table TableA with 3 records:
record 1:
id = 1, value = '<Employee id='1' name='Employee1'></Employee><Employee id='2' name='Employee2'></Employee>'
record 2:
id = 2, value = '<Employee id='1' name='Employee1'></Employee><Employee id='2' name='Employee2'></Employee><Employee id='3' name='Employee3'></Employee>'
record 3:
id = 3, value = '<Employee id='1' name='Employee1'></Employee><Employee id='2' name='Employee2'></Employee><Employee id='3' name='Employee3'></Employee><Employee id='4' name='Employee4'></Employee>'
the query:
SELECT * FROM TableA
WHERE...
How can I put the where clause to get only record 1?
Many thanks,
The problem with the data is that it doesn't contain well formed xml - you will need to wrap it before you can use the xml tools in Sql like xquery.
SELECT *
FROM
(
SELECT
Nodes.node.value('(./#id)[1]', 'int') AS EmployeeId,
Nodes.node.value('(./#name)[1]', 'varchar(50)') AS EmployeeName
FROM
(
SELECT CAST('<xml>' + value + '</xml>' AS Xml) As WrappedXml
FROM TableA
) AS x
cross apply x.WrappedXml.nodes('//Employee') as Nodes(node)
) as y
WHERE
y.EmployeeId = 1;
Inner select -wraps the xml
Middle select - standard xquery
Outer select - where filter
You haven't clarified what you mean w.r.t. get only record 1, but if you mean just the first element of each row (which coincidentally also has id = 1), you can use ROW_NUMBER() to assign a sequence:
SELECT *
FROM
(
SELECT
Nodes.node.value('(./#id)[1]', 'int') AS EmployeeId,
Nodes.node.value('(./#name)[1]', 'varchar(50)') AS EmployeeName,
ROW_NUMBER() OVER (PARTITION BY x.Id ORDER BY ( SELECT 1 )) SequenceId
FROM
(
SELECT Id, CAST('<xml>' + value + '</xml>' AS Xml) As WrappedXml
FROM TableA
) AS x
cross apply x.WrappedXml.nodes('//Employee') as Nodes(node)
) as y
WHERE SequenceId = 1;
Both Fiddles here
I tried with this query and It returns record 1 as I expect:
SELECT * FROM TableA
WHERE value.exist('(Employee[#id = 1])') = 1 and value.exist('(Employee[#id = 2])') = 1 AND value.value('count(Employee[#id])', 'int') = 2
Do you have any comments for this query? Should I use it? :)

Resources