SQL Server with left join/having sum/group by - sql-server

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)

Related

TSQL Compare tables based on multiple rows same column?

i will try and be as clear as possible on this one, as i have no idea what to do next and would love a kick in the right direction.
Im trying to compare the values within 2 tables. The tables look like this:
Table1:
Table2
INSERT INTO #table1 ([elementName], [elementValue])
VALUES
('t1','Project'),
('p1','test1'),
('n1','value1'),
('t2','Project'),
('p2','test2'),
('n2','value2'),
('t3','Project'),
('p3','test3'),
('n3','value3'),
('t4',''),
('p4',''),
('n4',''),
('t5',''),
('p5',''),
('n5','')
INSERT INTO #table2 ([elementName], [elementValue])
VALUES
('t1','Project'),
('p1',''),
('n1',''),
('t2','Project'),
('p2','test3'),
('n2','value123'),
('t3','Project'),
('p3',''),
('n3',''),
('t4','Package'),
('p4',''),
('n4',''),
('t5','Project'),
('p5','Testtest'),
('n5','valuevalue')
I used this code to fill the testtables. Normally this is an automated process, and the tables are filled from an XML string.
Furthermore, the numbers in the element name are considered "groups" meaning T1 P1 and N1 are together.
I would like to compare T1 and P1 etc from Table1 to any combination of T and P from table2
If they match, i would like to overwrite the value of Table 1 N1 with the value of the matched N on table 2. (in the example, table1 N3 would be replaced with table2 N2
Besides that i also want to keep every group in table 1 that is not in table 2
but also add every group that is in table 2 but not in table 1 on one of the blank spots.
Last but not least, if the T value is filled, but P value is empty, it does not have to overwrite/change anything in table1.
The expected result would be this:
Table1:
i made the changes bold.
I dont really have an idea on where to start on this. Ive tried functions as except and intersect, but did not get even close to what i would like to see.
with t1 as (
select * from (values
('t1','Project'),
('p1','test1'),
('n1','value1'),
('t2','Project'),
('p2','test2'),
('n2','value2'),
('t3','Project'),
('p3','test3'),
('n3','value3'),
('t4',''),
('p4',''),
('n4',''),
('t5',''),
('p5',''),
('n5','')
) v([elementName], [elementValue])
),
t2 as (
select * from (values
('t1','Project'),
('p1',''),
('n1',''),
('t2','Project'),
('p2','test3'),
('n2','value123'),
('t3','Project'),
('p3',''),
('n3',''),
('t4','Package'),
('p4',''),
('n4',''),
('t5','Project'),
('p5','Testtest'),
('n5','valuevalue')
) v([elementName], [elementValue])
),
pivoted_t1 as (
select *
from
(select left([elementName], 1) letter, right([elementName], len([elementName]) - 1) number, [elementValue] as value from t1) t1
pivot(min(value) for letter in ([t], [p], [n])) pvt1
),
pivoted_t2 as (
select *
from
(select left([elementName], 1) letter, right([elementName], len([elementName]) - 1) number, [elementValue] as value from t2) t2
pivot(min(value) for letter in ([t], [p], [n])) pvt2
),
amended_values as (
select
pvt1.number,
coalesce(pvt2.t, pvt1.t) as t,
coalesce(pvt2.p, pvt1.p) as p,
coalesce(pvt2.n, pvt1.n) as n,
count(case when pvt1.t = '' and pvt1.p = '' then 1 end) over(order by pvt1.number rows between unbounded preceding and current row) as empty_row_number
from
pivoted_t1 pvt1
left join pivoted_t2 pvt2 on pvt1.t = pvt2.t and pvt1.p = pvt2.p and pvt1.t <> '' and pvt1.p <> ''
),
added_new_values as (
select
a.number,
coalesce(n.t, a.t) as t,
coalesce(n.p, a.p) as p,
coalesce(n.n, a.n) as n
from
amended_values a
left join (
select number, t, p, n, row_number() over (order by number) as row_number
from pivoted_t2 t2
where
t2.t <> ''
and t2.p <> ''
and not exists (select * from pivoted_t1 t1 where t1.t = t2.t and t1.p = t2.p)
) n on n.row_number = a.empty_row_number
)
select
concat([elementName], number) as [elementName],
[elementValue]
from
added_new_values
unpivot ([elementValue] for [elementName] in ([t], [p], [n])) upvt
;

SQL update from one table to another based on a group by condition

I have two tables that only reference one column between them.
Equipment(E)
key
Job(J)
key
name
isdeleted
Table E needs to be updated with the minimum J.Key with the same J.Name where J.Deleted = 0. So in the following, E.Key = 18 would be updated to 12.
E
18
3
12, "This", 0
6, "This", 1
18, "This", 0
3, "That", 0
I think I need to update using min(key) and grouping by name and key where isdeleted - 0 but cannot figure how to put all of that together.
Please try below query to update E table (Please test it before executing it on PROD by doing select first instead of update)-
update E set
E.[key] =
(select min(J1.[key]) from Job J1 where J1.name = J.name and J1.isdeleted = 0)
from Equipment E
inner join Job J on J.[key] = E.[key]
You can use group by statements, as a previous user posted, but I thought I'd give you another option.
;with mins as
(
select a.pid
,b.pid min_pid
from #j a
cross apply (
select top 1
b.*
from #j b
where b.name = a.name
and b.isdeleted = 0
order by b.pid
) b
where a.isdeleted = 0
)
update a
set pid = b.min_pid
from #e a
inner join mins b on b.pid = a.pid
Slightly different approach using a CTE to get the minimums:
WITH jobUpdates AS
(
SELECT [key],
MIN([key]) OVER (Partition By Name) AS MinKey
FROM jobs
WHERE IsDeleted = 0
)
UPDATE e
SET e.[Key]
FROM equipment e
INNER JOIN jobUpdates j
ON j.[key] = e.[key];

Instead of NULL how do I show `0` in result of a pivot table in SQL SERVER

In my #disttable for some columns result is showing as null. Instead, I want to show 0. I want to show 'O' in Pivot table in place of NULL. I tried to give isnull for Total..But it did not work properly.
declare #DistTable Table
(
Party nvarchar(200),
DistName nvarchar(200),
Total int,
TotalSeats int,
DeclaredSeats int
)
insert into #DistTable
SELECT C.English AS Party,f.English as DistNAME,count(C.English) as Total,
TOTALSEATS=(SELECT COUNT(*) FROM TBL_CONSTITUENCYMASTER c WHERE c.Phase= 3 and c.StateCode=29 and c.reg_code = 61),
DECLAREDSEATs=(SELECT COUNT(*) FROM TBL_CONSTITUENCYMASTER c WHERE c.Phase= 3 and c.StateCode=29 and c.reg_code = 61 and Lead_WonCode=100)
FROM TBL_CONSTITUENCYMASTER A
LEFT OUTER JOIN tbl_CandidateMaster B ON A.Lead_CandiCode = B.Cand_Code
LEFT OUTER JOIN tbl_AllianceMaster C ON B.AllianceCode = C.AllianceCode
join tbl_regionmaster f on a.reg_code=f.reg_code
join tbl_olddistrictMaster e on a.Old_dist_code=e.old_dist_code
join tbl_DistrictMaster D on A.Dist_Code=D.Dist_Code WHERE A.STATECODE = 29 and A.Phase = 3 and A.Lead_WonCode = 100 and f.reg_code = 61 group by c.English,f.English order by F.English
select * from #DistTable pivot (min(Total) for Party in([TRS],[INC],[TDP],[BJP],[CPI],[CPM],[OTH])) as t
The result is
ADILABAD 163 163 67 27 NULL NULL NULL NULL 69
Instead of NULL how do I show 0 in result of a pivot table in SQL SERVER?
I would do conditional aggregation instead :
SELECT dt.DistName,
min(case when dt.Party = 'TRS' then dt.Total else 0 end) as TRS,
. . .
FROM #DistTable dt
GROUP BY dt.DistName;

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

Joining two queries horizontally

I wrote two queries below that produce one row of data each.
What is the best way to combine them such that I am LEFT with only a single row of data?
These are coming FROM two DISTINCT databases named : [ASN01] and [dsi_ASN_dsicx]
I have 70 pairs of databases like this but am showing only one for simplicity.
The fact that the three letter acronym ASN is common to both database names is no mistake and if needed can be a part of the solution.
Current Results:
Site, Elligence (header)
ASN, 100.00
Site, GP_Total (header)
ASN, 120.00
Desired results:
Site, GP_Total, Elligence (header)
ASN, 120.00, 100.00
SELECT 'ASN' AS Site ,
CASE SUM(perdblnc)
WHEN NULL THEN 0
ELSE -1 * SUM(PERDBLNC)
END AS GP_Total
FROM [ASN01].[dbo].[GL10110] T1
LEFT OUTER JOIN [ASN01].[dbo].[GL00105] T2 ON [T1].[ACTINDX] = [T2].[ACTINDX]
WHERE YEAR1 = 2012
AND PERIODID IN ( '2' )
AND ACTNUMST IN ( '4200-0000-C', '6940-0000-C', '6945-0000-C',
'6950-0000-C' )
SELECT 'ASN' AS [Site] ,
SUM(pi.amount) AS [Elligence]
FROM [dsi_ASN_dsicx].dbo.charge c
LEFT JOIN [dsi_ASN_dsicx].dbo.paymentitem pi ON c.idcharge = pi.chargeid
LEFT JOIN [dsi_ASN_dsicx].dbo.payment p ON pi.paymentid = p.idpayment
LEFT JOIN [dsi_ASN_dsicx].dbo.paymenttype pt ON p.paymenttypeid = pt.idpaymenttype
WHERE pi.amount != 0
AND pt.paymentmethod NOT IN ( '5', '7' )
AND pt.paymentmethod IS NOT NULL
AND p.sdate >= '20120201'
AND p.sdate <= '20120229'
WIthout going through and changing any of your queries, the easiest way would be to use temp tables using the "WITH" common_table_expression. Table1 and Table2 are temp tables created from your select statements. Therefore, we select table1 and join table2.
Let me know if there are any syntax problems, I don't have anything to test this on presently.
;With Table1 as (SELECT 'ASN' as Site, Case sum(perdblnc)
WHEN NULL THEN 0
ELSE -1*sum(PERDBLNC) END as GP_Total
FROM [ASN01].[dbo].[GL10110] T1
Left Outer Join [ASN01].[dbo].[GL00105] T2
ON [T1]. [ACTINDX]= [T2]. [ACTINDX]
WHERE YEAR1 = 2012
AND PERIODID in ('2')
AND ACTNUMST in ('4200-0000-C', '6940-0000-C', '6945-0000-C', '6950-0000-C'))
, Table2 as (SELECT
'ASN' as [Site],
SUM(pi.amount) as [Elligence]
FROM [dsi_ASN_dsicx].dbo.charge c
LEFT JOIN [dsi_ASN_dsicx].dbo.paymentitem pi on c.idcharge = pi.chargeid
LEFT JOIN [dsi_ASN_dsicx].dbo.payment p on pi.paymentid = p.idpayment
LEFT JOIN [dsi_ASN_dsicx].dbo.paymenttype pt on p.paymenttypeid = pt.idpaymenttype
WHERE pi.amount != 0
AND pt.paymentmethod not in ('5','7')
AND pt.paymentmethod is not null
AND p.sdate >='20120201' and p.sdate <= '20120229')
SELECT * FROM Table1
LEFT JOIN Table2 ON Table1.site = Table2.site
Hope this helps! Marks as answer if it is =)

Resources