Struggling with conditional logic - sql-server

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
;

Related

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

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

SQL Server 2012 join query

I have 2 tables: Fields{id, testid, labid, upperlimit, lowerlimit) and LabTest{id, testid, labid, upperlimit, lowerlimit)
Sample data: Fields: 0, testid1, 1, 10, 20
1, testid2, 2, 10, 20
LabTest: 0, testid1, 1, 40, 50
1, testid1, null, 50, 60
2, testid2, null, 55, 67
3, testid2, null, 67, 78
4, testid2, null, 78, 89
join on testid1 should return 1 line
join on testid2 should return 3 lines
If labid in Fields table is not null and testid is not null then I need to use the upperlimit and lowerlimit of the LabTest table where Fields.testid=LabTest.testid and Fields.labid=LabTest.labid
If I use a left join (because I need to take the Fields without testid also):
select * from Fields left join LabTest on Fields.testid=LabTest.testid
I will get back all the fields no matter if their labid is null or not (5 lines using the above sample data), but
what I want to get is only one line the one with LabTest.labid not
null and if this doesnt exist then and only then I want to get the
line(s) with LabTest.labid = null
Use Inner Join or If filter the Left join with a Where Clause
;WITH CTE
AS
(
select
RN = ROW_NUMBER() OVER(PARTITION BY Fields.Id ORDER BY labid),
Fields_Id = Fields.id,
Fields_testid = Fields.testid,
Fields_labid = Fields.labid,
Fields_upperlimit = Fields.upperlimit,
Fields_lowerlimit = Fields.lowerlimit,
LabTest_Id = LabTest.id,
LabTest_testid = LabTest.testid,
LabTest_labid = LabTest.labid,
LabTest_upperlimit = LabTest.upperlimit,
LabTest_lowerlimit = LabTest.lowerlimit
from Fields
left join LabTest
on Fields.testid=LabTest.testid
)
SELECT
*
FROM CTE
WHERE RN = 1
Try this:
select top 1 *
from Fields left join LabTest
on Fields.testid=LabTest.testid
where LabTest.labid is not null

SQL join to an alias column name in SQL Server

Not sure if this is possible but I'm trying to join to an alias column in SQL Server.
Is it possible to change the JOIN below?
JOIN
#LOGFILE ON #CSIQUESTIONLOG.LogSeqNo = #LOGFILE.Seq
AND #LOGFILE.Contcode = 'WCM'
to
JOIN
#LOGFILE ON #CSIQUESTIONLOG.LogSeqNo = #LOGFILE.[SEQ2]
AND #LOGFILE.Contcode = 'WCM'
So it joins to the SEQ2 column - not the SEQ column, and so I only have to run one query
Sample data below:
IF OBJECT_ID('tempdb..#CSIQUESTIONLOG') IS NOT NULL
DROP TABLE #CSIQUESTIONLOG
SELECT *
INTO #CSIQUESTIONLOG
FROM (VALUES ('BA', '2017-01-01','123451', '185', 2),
('BA', '2017-01-01','123452', '185', 4),
('BA', '2017-01-01','123453', '184', 1),
('BA', '2017-01-01','123454', '183', 3),
('BA', '2017-01-01','123455', '182', 5),
('BA', '2017-01-01','123456', '181', 0),
('BA', '2017-01-01','123457', '182', 1),
('BA', '2017-01-01','7684417', '180', 2)) d (Dealer, Created, Logseqno, CSIseqno, Answer)
IF OBJECT_ID('tempdb..#LOGFILE') IS NOT NULL
DROP TABLE #LOGFILE
SELECT *
INTO #LOGFILE
FROM (VALUES (7684417, 'BA', 498, 'WCM', 1261723),
(7669984, 'BA', 38, 'CSI', 1261723),
(7685141, 'BA', 400, 'WCM', 1261750),
(7686369, 'BA', 193, 'CSI', 1261750),
(7692571, 'BA', 401, 'WCM', 1262289),
(7700336, 'BA', 38, 'CSI', 1262289)) d (Seq, Dealer, OpNum, Contcode, ContSeqNo)
SELECT
a.*, x.Seq AS [SEQ2]
FROM
#LOGFILE a
OUTER APPLY
(SELECT Seq
FROM #LOGFILE b
WHERE b.ContSeqNo = a.ContSeqNo AND b.ContCode = 'CSI') x
Final query:
SELECT
#CSIQUESTIONLOG.Created, #CSIQUESTIONLOG.CSIseqno,
#LOGFILE.OpNum,
COUNT (*) AS TOTAL
FROM
#CSIQUESTIONLOG
JOIN
#LOGFILE ON #CSIQUESTIONLOG.LogSeqNo = #LOGFILE.Seq
AND #LOGFILE.Contcode = 'WCM'
GROUP BY
#CSIQUESTIONLOG.Created, #CSIQUESTIONLOG.CSIseqno, #LOGFILE.OpNum
Firstly, in your final query there ins no column Seq2. Those are two seperate queries, and thus, they can't interact with each other.
Without the expected result set, I've just chanegd the SQL to what I believe you're after. If it's not, post your expected result set.
SELECT QL.Created,
QL.CSIseqno,
LF2.OpNum, --Not sure if this should be LF1, or LF2. Guessed 2
COUNT (*) AS TOTAL
FROM #CSIQUESTIONLOG QL
JOIN #LOGFILE LF1 ON QL.LogSeqNo = LF1.Seq AND LF1.Contcode = 'WCM'
JOIN #LOGFILE LF2 ON LF1.ContSeqNo = LF2.ContSeqNo AND LF2.ContCode = 'CSI'
GROUP BY QL.Created, --You were completely missing your GROUP BY
QL.CSIseqno,
LF2.OpNum;

T-SQL- Recursive query- employee hierarchy

I want to use recursive query (it can be cte or anything) where I am trying to pull the executive report - where I input one employee name and I want all the hierarchy related to that employees starting with the top level emloyee (starting with CEO) and then going down.
For example:
If I input the employee name Celia, the report should look like:
CEO
Employees reporting to CEO , let's say MgrX
Employees reporting to MgrX- let's say MgrY
Employees reporting to MgrY - let's say MgrZ
All the Employees reporting to MgrZ including Celia (input parameter).
The query I am trying to use :
with cte1 as
(
select
pa.PERSNBR
,pa.AUTHCD
,pu.VALUE
,hr.File#
,hr.[Payroll Name]
,hr.[Reports To File#]
,hr.[Reports To Name]
,hr.[EMC #]
,hr.[EMC Name]
from
[DNA_Staging].[dbo].[PERSAUTH] pa
join [DNA_Staging].[dbo].[PERSEMPL] pe
on pa.PERSNBR = pe.PERSNBR
join [DNA_Staging].[dbo].[PERSUSERFIELD] pu
on pe.PERSNBR = pu.PERSNBR
and pu.USERFIELDCD = 'EFNR'
and GETDATE() < isnull(pe.inactivedate,getdate()+1)
join [HR_Staging].[dbo].[HR_EmployeeHierarchyStaging] hr
on pu.VALUE = substring(hr.File#,2,6)
or pu.VALUE = substring(hr.File#,3,6)
),
-- find all the data for input payroll name in the parameter
cte2 as (select *
FROM cte1 where [Payroll Name] = 'Acain, Celia T'),
A basic example for your question (you haven't provide any data samples or output examples, so...):
DECLARE #name nvarchar(20) = 'Darkwater'
;WITH emplevels AS (
SELECT *
FROM (VALUES
(1, 'CEO'),
(2, 'MgrX'),
(3, 'MgrY'),
(4, 'MgrZ')
) as t(eid, who)
),
people AS ( --people and there levels
SELECT *
FROM (VALUES
(1, 'Smith', 1),
(2, 'Wake', 2),
(3, 'Cartman', 3),
(4, 'Sanders', 4),
(5, 'MacCormic', 1),
(6, 'Malkovich', 2),
(7, 'Whatever', 2),
(8, 'Darkwater', 3),
(9, 'Leto', 4)
) as t(pid, name, eid)
),
dep AS ( --dependences of levels
SELECT *
FROM (VALUES
(1,1), --CEO - CEO
(1,2), --CEO - MgrX
(2,3), --MgrX - MgrY
(3,4) --MgrY - MgrZ
)as t(eid1,eid2)
),
hier as ( -- get the hierarchy
SELECT d.eid1,
d.eid2
FROM dep d
LEFT JOIN people p ON d.eid2 = p.eid
where name = #name
UNION ALL
SELECT d.eid1,
d.eid2
FROM dep d
INNER JOIN hier h ON d.eid2 = h.eid1
WHERE d.eid2 != d.eid1
)
--here comes heads and who there are
SELECT p.name AS Head,
e.who AS WhoIs
from hier h
LEFT JOIN people p ON p.eid = h.eid1
LEFT JOIN emplevels e ON e.eid = p.eid
ORDER BY e.eid
Result for Darkwater (he is MgrY). We have all MgrX and CEO's:
Head WhoIs
--------- -----
Smith CEO
MacCormic CEO
Wake MgrX
Malkovich MgrX
Whatever MgrX
(5 row(s) affected)

SQL Server with left join/having sum/group by

Table B holds planned values. Table M hold actual values. I need to find all rows in table B where either there is no actual values (ie. joined) row in table M, or where joined rows have different total actual value rows. I am trying a combination of an outer join and having sum ... group by to achieve this, but it isn't working because the 'orphans' in table B are not being returned.
My query is:-
SELECT B.Id, B.Date, b.Ref,SUM(M.Actual_Volume), SUM(B.Planned_Volume),
SUM(M.Actual_Value),SUM(B.Planned_Value)
FROM
TableB B
left JOIN TableM M on M.Id = B.Id
inner JOIN TableX on TableX.FieldX = B.FieldX
WHERE TableX.FieldY = (SELECT T.FieldY from TableX T where T.FieldX = 408344)
AND TableX.FieldZ = (SELECT T1.FieldZ from TableX T1 where T1.FieldX = 408344)
group by B.Id, B.Date, B.Ref
having SUM(M.Actual_Volume) <> SUM(B.Planned_Volume)
OR SUM(M.Actual_Value) <> SUM(B.Planned_Value)
order by b.Id
If I use = instead of <> to compare the actuals and planned I get rows that join, but I need the rows where the actuals don't equal the planned, or where there is a planned but not an actual.
Thanks!
Table B
Id planned_vol planned val
19 2 350
28 1 100
53 3 650
61 1 50
Table M
M.Id B.Id actual_vol actual_val
58 19 2 350
65 28 1 100
66 53 1 150
So the query should return,
B.Id
53 (because planned_vol <> actual_vol and planned_val <> actual_val)
61 (because B.Id 61 is not in table M)
hth!
This is untested, but I think you need to move the having requirements into the left outer join requirements. Using CTEs (i.e. you need to be using SQL Server 2005 or later for this to work) is one way to do it.
Your having clause is forcing SQL Server to treat the B-M join as an inner join. There may be an alternative approach that does not use CTEs which checks for NULLs in all the right places. But I prefer the divide-and-conquer approach.
WITH
[BAlt] AS
(
SELECT
[B].[Id],
[B].[Date],
[B].[Ref],
SUM([B].[Planned_Volume]) AS [Planned_Volume],
SUM([B].[Planned_Value]) AS [Planned_Value],
FROM [TableB] AS [B]
INNER JOIN [TableX] AS [X1] ON [X1].[FieldX] = [B].[FieldX]
AND [X1].[FieldY] =
(
SELECT
[X2].[FieldY]
FROM [TableX] AS [X2]
WHERE [X2].[FieldX] = 408344
)
AND [X1].[FieldZ] =
(
SELECT
[X3].[FieldZ]
FROM [TableX] AS [X2]
WHERE [X3].[FieldX] = 408344
)
GROUP BY
[B].[Id],
[B].[Date],
[B].[Ref]
),
[MAlt] AS
(
SELECT
[M].[Id],
SUM([M].[Actual_Volume]) AS [Actual_Volume],
SUM([M].[Actual_Value]) AS [Actual_Value]
FROM [M]
GROUP BY
[M].[Id]
)
SELECT
[BAlt].[Id],
[BAlt].[Date],
[BAlt].[Ref],
[BAlt].[Planned_Volume],
[BAlt].[Planned_Value],
[MAlt].[Actual_Volume],
[MAlt].[Actual_Value]
FROM [BAlt]
LEFT OUTER JOIN [MAlt] ON [MAlt].[Id] = [BAlt].[Id]
AND
(
[MAlt].[Actual_Volume] <> [BAlt].[Planned_Volume]
OR [MAlt].[Actual_Value] <> [BAlt].[Planned_Value]
)
ORDER BY
[BAlt].[Id]
I really don't see a problem:
create table b
( B_id int
,PlannedVolume int
,PlannedValue int
)
create table M
( M_id int
,B_id int
,ActualVolume int
,ActualValue int
)
insert b (b_id, PlannedVolume, PlannedValue)
values (19, 2, 350),
(28, 1, 100),
(53, 3, 650),
(61, 1, 50)
insert m (m_id, b_id, ActualVolume, ActualValue)
values (58, 19, 2, 350),
(65, 28, 1, 100),
(66, 53, 1, 150),
(67, 53, 1, 100)
select b.b_id
from b
left join
( select b_id
,sum(ActualVolume) as ActualVolume
,sum(ActualValue) as ActualValue
from m
group by b_id
) m
on m.b_id = b.b_id
where
m.b_id is null
or
(m.ActualValue <> b.PlannedValue and m.ActualVolume <> b.PlannedVolume)

Resources