Join 3 temp tables into 1 result table by merging columns - sql-server

I have a huge query that creates a cte of warehouses and uses this to create 3 more cte's with the same column names.
below is my T-Sql query:
With WhList AS (SELECT Company, Warehouse FROM
CCWMS.dbo.WMSWarehouse_Control),
CoB AS (SELECT 'B' AS Company, B_IMB.Warehouse, B_IMB.StockCode, B_IMB.Bin, B_IMB.QtyOnHand1 AS QtyOnHand, B_IMB.SoQtyToShip, B_IMB.LastReceiptDate,B_IMB.OnHold FROM SysproCompanyB.dbo.InvMultBin AS B_IMB RIGHT JOIN WhList As WL ON B_IMB.Warehouse=WL.Warehouse WHERE QtyOnHand1 <> 0),
CoX AS (SELECT 'X' AS Company, X_IMB.Warehouse, X_IMB.StockCode, X_IMB.Bin, X_IMB.QtyOnHand1 AS QtyOnHand, X_IMB.SoQtyToShip, X_IMB.LastReceiptDate,X_IMB.OnHold FROM SysproCompanyX.dbo.InvMultBin AS X_IMB RIGHT JOIN WhList As WL ON X_IMB.Warehouse=WL.Warehouse WHERE QtyOnHand1 <> 0),
CoH AS (SELECT 'H' AS Company, H_IMB.Warehouse, H_IMB.StockCode, H_IMB.Bin, H_IMB.QtyOnHand1 AS QtyOnHand, H_IMB.SoQtyToShip, H_IMB.LastReceiptDate,H_IMB.OnHold FROM SysproCompanyH.dbo.InvMultBin AS H_IMB RIGHT JOIN WhList As WL ON H_IMB.Warehouse=WL.Warehouse WHERE QtyOnHand1 <> 0)
I need to compile these three cte's into one result set.
Any way I might be able to achieve this. I have thought about PIVOT but cant seem to wrap my head around how to do this.

I think you are looking for union all of all the results... What do you mean by combining the results?
Are you looking something like below?
SELECT Company, Warehouse FROM (
SELECT 'B' AS Company, B_IMB.Warehouse, B_IMB.StockCode, B_IMB.Bin, B_IMB.QtyOnHand1 AS QtyOnHand, B_IMB.SoQtyToShip, B_IMB.LastReceiptDate,B_IMB.OnHold FROM SysproCompanyB.dbo.InvMultBin AS B_IMB RIGHT JOIN WhList As WL ON B_IMB.Warehouse=WL.Warehouse WHERE QtyOnHand1 <> 0
UNION ALL
SELECT 'X' AS Company, X_IMB.Warehouse, X_IMB.StockCode, X_IMB.Bin, X_IMB.QtyOnHand1 AS QtyOnHand, X_IMB.SoQtyToShip, X_IMB.LastReceiptDate,X_IMB.OnHold FROM SysproCompanyX.dbo.InvMultBin AS X_IMB RIGHT JOIN WhList As WL ON X_IMB.Warehouse=WL.Warehouse WHERE QtyOnHand1 <> 0
UNION ALL
SELECT 'H' AS Company, H_IMB.Warehouse, H_IMB.StockCode, H_IMB.Bin, H_IMB.QtyOnHand1 AS QtyOnHand, H_IMB.SoQtyToShip, H_IMB.LastReceiptDate,H_IMB.OnHold FROM SysproCompanyH.dbo.InvMultBin AS H_IMB RIGHT JOIN WhList As WL ON H_IMB.Warehouse=WL.Warehouse WHERE QtyOnHand1 <> 0
) A

I solved it. I cant believe I didn't think of this. Answer as follows:
--CTE's end here.
SELECT * FROM CoB
UNION
SELECT * FROM CoX
UNION
SELECT * FROM CoH
Thanks though

Related

Need the sum of two columns from subquery

I have here my query and subquery to generate totals for the types I'm looking for in my database but now I need to somehow get the total for the entire columns. I feel like the solution is right in front of me but I cannot figure it out. Any step in the right direction would be greatly appreciated.
SELECT
SUM(b.aGiven) AS given,
SUM(b.aUsed) AS used
FROM UserAccountGroups AS uag
LEFT OUTER JOIN (
SELECT
uac1.UserAccountGroupID AS aGroupID,
SUM(ua.UserAccountUsedAmount) AS aUsed,
0 AS aGiven
FROM UserAccounts AS ua
LEFT OUTER JOIN UserAccountCodes AS uac1 ON ua.UserAccountCode = uac1.UserAccountCode
WHERE uac1.UserAccountCodeCreatedOn between '07-11-2020' and '07-11-2021'
GROUP BY uac1.UserAccountGroupID
UNION
SELECT
uac.UserAccountGroupID AS aGroupID,
0 AS aUsed,
SUM(uac.UserAccountCodeAmount) AS aGiven
FROM UserAccountCodes AS uac
LEFT OUTER JOIN UserAccounts AS ua1 ON uac.UserAccountCode = ua1.UserAccountCode
WHERE uac.UserAccountCodeCreatedOn between '07-11-2010' and '07-11-2021'
GROUP BY uac.UserAccountGroupID
) AS b ON b.aGroupID = uag.UserAccountGroupID
GROUP BY uag.ReportGroup
*** Update ***
I'm sorry if my question was unclear. This is a query I am using to pull the totals for each type of 'ReportGroup' from the db. Now, rather than needing the totals per group, I need the totals per the column. The idea is to pass in date variables in my codebehind to pull from custom dates and now I would like to have a grand total per column at the bottom of my report. I know I don't need to select any data from UserAccountGroups but I'm having trouble re-working the query to get accurate results. A point in the right direction would be very helpful and thank you beforehand.
If you can provide DML and DDL statements this could have tested easily. This will give sum for each aGroupID and grand total for columns aUsed, aGiven.
SELECT *
into #temp_agg_data
FROM
(
SELECT
uac1.UserAccountGroupID AS aGroupID,
SUM(ua.UserAccountUsedAmount) AS aUsed,
0 AS aGiven
FROM UserAccounts AS ua
LEFT OUTER JOIN UserAccountCodes AS uac1 ON ua.UserAccountCode = uac1.UserAccountCode
WHERE uac1.UserAccountCodeCreatedOn between '07-11-2020' and '07-11-2021'
GROUP BY uac1.UserAccountGroupID
UNION ALL
SELECT
uac.UserAccountGroupID AS aGroupID,
0 AS aUsed,
SUM(uac.UserAccountCodeAmount) AS aGiven
FROM UserAccountCodes AS uac
LEFT OUTER JOIN UserAccounts AS ua1 ON uac.UserAccountCode = ua1.UserAccountCode
WHERE uac.UserAccountCodeCreatedOn between '07-11-2010' and '07-11-2021'
GROUP BY uac.UserAccountGroupID
) AS A
--Last select will list down all records with sum group by [aGroupID] and grand total for [aUsed] , [aGiven] as a row below as 'Grand Total'
SELECT
[aGroupID]
,[aUsed]
,[aGiven]
FROM
#temp_agg_data
UNION ALL
SELECT
'Grand Total' AS [aGroupID]
, SUM(aUsed) as [aUsed]
, SUM(aUsed) as [aGiven]
FROM #temp_agg_data
DROP TABLE #temp_agg_data
You need to take out UserAccountGroupID in the select statements in order to get the Sum of all. Right now, you have UserAccountGroupID in the select, so it is summing by UserAccountGroupID. Hope this answers your question.
the reportGroup has not be used in the join with the subqueries so it can not be grouped by in the result query. Add the reportGroup to filter to the subqueries in the union. You don't need a union or union all sql.
declare #UserAccounts as table(UserAcccountGroupID int,UserAccountCode varchar(10),UserAccountUsedAmount money)
declare #UserAccountCodes as table (UserAccountCode varchar(10),UserAccountCodeCreatedOn Date,UserAccountGroupID int,UserAccountCodeAmount money)
declare #UserAccountGroups as table(UserAccountGroupID int,ReportGroup varchar(10));
select
IsNull(UserAccountCodes.aGiven,0) aGiven
,IsNull(UserAccounts.aUsed,0) aUsed
FROM #UserAccountGroups AS uag
OUTER APPLY
(
SELECT
--uac1.UserAccountGroupID AS aGroupID,
SUM(ua.UserAccountUsedAmount) AS aUsed
--0 AS aGiven
FROM #UserAccounts AS ua
LEFT JOIN #UserAccountCodes AS uac1
ON ua.UserAccountCode = uac1.UserAccountCode
WHERE uac1.UserAccountCodeCreatedOn between '07-11-2020' and '07-11-2021'
and uac1.UserAccountGroupID = uag.UserAccountGroupID
) UserAccounts
outer apply
(
SELECT
--uac.UserAccountGroupID AS aGroupID,
--0 AS aUsed,
SUM(uac.UserAccountCodeAmount) AS aGiven
FROM #UserAccountCodes AS uac
LEFT JOIN #UserAccounts AS ua1
ON uac.UserAccountCode = ua1.UserAccountCode
WHERE uac.UserAccountCodeCreatedOn between '07-11-2010' and '07-11-2021'
and uac.UserAccountGroupID = uag.UserAccountGroupID
) UserAccountCodes

SQL Server : trying to insert a count of zero when a record doesn't exist

I am trying to modify the results of a query to populate a zero when a certain status doesn't exist.
In my base result I have something that looks like this:
But when a certain example doesn't appear in my table, I need a way to have a row show up with a zero for reporting needs, something like this:
I was trying to use a CTE maybe to populate those and left join it up...but doesn't seem to be working the way I want.
WITH DummyValues AS
(
SELECT 'Yellow' AS Val
UNION ALL
SELECT 'Red'
UNION ALL
SELECT 'Gray'
)
SELECT D.Val, V.PlntCd, COUNT(UpgradeMeasure)
FROM reporting.vw_SOTAgingView V
LEFT OUTER JOIN DummyValues D ON D.Val = V.UpgradeMeasure
GROUP BY D.Val, V.PlntCd
Is this an easy thing I am just missing something simple?
You can use a LEFT OUTER JOIN like this to always include the statuses (I switched the order of the tables since that is usually easier to read for most people):
SELECT
D.Val,
V.PlntCd,
COALESCE(COUNT(UpgradeMeasure), 0) AS [Count]
FROM (SELECT 'Yellow' UNION ALL SELECT 'Red' UNION ALL SELECT 'Gray') D
LEFT OUTER JOIN reporting.vw_SOTAgingView V
ON D.Val = V.UpgradeMeasure
GROUP BY D.Val, V.PlntCd
Just note that this won't exactly get your desired set. The "PlntCd" will be NULL if no match is found. If you want to ensure you cover all your plants, you need to start with a complete listing of plants and CROSS JOIN that source to statuses first. This might look like:
SELECT
D.Val, -- From cross-join
P.PlntCd, -- From source
COALESCE(COUNT(UpgradeMeasure), 0) AS [Count]
FROM (SELECT DISTINCT PlntCd FROM reporting.vw_SOTAgingView) P
CROSS JOIN (SELECT 'Yellow' UNION ALL SELECT 'Red' UNION ALL SELECT 'Gray') D
LEFT OUTER JOIN reporting.vw_SOTAgingView V
ON D.Val = V.UpgradeMeasure
AND P.PlntCd = V.PlntCd -- Also join to source to prevent dupes
GROUP BY D.Val, P.PlntCd -- Use source plant code
You have the join backwards.
You left join against the subset. (Or do it the way you have it and RIGHT OUTER JOIN, except no one really uses right joins)
SELECT
*
FROM
TableWithAllData All
LEFT JOIN TableWithSomeData Some ON Some.Id = All.id

Using the results of WITH clause IN where STATEMENT of main query

I am relatively new at SQL so I apologise if this is obvious but I cannot work out how to use the results of the WITH clause query in the where statement of my main query.
My with query pulls the first record for each customer and gives the sale date for that record:
WITH summary AS(
SELECT ed2.customer,ed2.saledate,
ROW_NUMBER()OVER(PARTITION BY ed2.customer
ORDER BY ed2.saledate)AS rk
FROM Filteredxportdocument ed2)
SELECT s.*
FROM summary s
WHERE s.rk=1
I need to use the date in the above query as the starting point and pull all records for each customer for their first 12 months i.e. where the sale date is between ed2.saledate AND ed2.saledate+12 months.
My main query is:
SELECT ed.totalamountincvat, ed.saledate, ed.name AS SaleRef,
ed.customer, ed.customername, comp.numberofemployees,
comp.companyuid
FROM exportdocument AS ed INNER JOIN
FilteredAccount AS comp ON ed.customer = comp.accountid
WHERE (ed.statecode = 0) AND
ed.saledate BETWEEN ed2.saledate AND DATEADD(M,12,ed2.saledate)
I am sure that I need to add the main query into the WITH clause but I cant work out where. Is anyone able to help please
Does this help?
;WITH summary AS(
SELECT ed2.customer,ed2.saledate,
ROW_NUMBER()OVER(PARTITION BY ed2.customer
ORDER BY ed2.saledate)AS rk
FROM Filteredxportdocument ed2)
SELECT ed.totalamountincvat, ed.saledate, ed.name AS SaleRef,
ed.customer, ed.customername, comp.numberofemployees,
comp.companyuid
FROM exportdocument AS ed INNER JOIN
FilteredAccount AS comp ON ed.customer = comp.accountid
OUTER APPLY (SELECT s.* FROM summary s WHERE s.rk=1) ed2
WHERE ed.statecode = 0 AND
ed.saledate BETWEEN ed2.saledate AND DATEADD(M,12,ed2.saledate)
and ed.Customer = ed2.Customer
Results of CTE are not cached or stored, so you can't reuse it.
EDIT:
Based upon your requirement that all the records from CTE should be in final result, this is a new query:
;WITH summary AS(
SELECT ed2.customer,ed2.saledate,
ROW_NUMBER()OVER(PARTITION BY ed2.customer
ORDER BY ed2.saledate)AS rk
FROM Filteredxportdocument ed2)
SELECT
ed.totalamountincvat,
ed.saledate,
ed.name AS SaleRef,
ed.customer,
ed.customername,
comp.numberofemployees,
comp.companyuid
FROM
summary ed2
left join exportdocument ed
on ed.Customer = ed2.Customer
and ed.statecode = 0
AND ed.saledate BETWEEN ed2.saledate AND DATEADD(M,12,ed2.saledate)
INNER JOIN FilteredAccount comp
ON ed.customer = comp.accountid
WHERE
s.rk=1
summary you will be able to use only once. Alternate solution is store summary into temp table and use that as many times as u want.
Something like : Select * into #temp from Summary s where s.rk=1

TSQL optimizing code for NOT IN

I inherit an old SQL script that I want to optimize but after several tests, I must admit that all my tests only creates huge SQL with repetitive blocks. I would like to know if someone can propose a better code for the following pattern (see code below). I don't want to use temporary table (WITH). For simplicity, I only put 3 levels (table TMP_C, TMP_D and TMP_E) but the original SQL have 8 levels.
WITH
TMP_A AS (
SELECT
ID,
Field_X
FROM A
TMP_B AS(
SELECT DISTINCT
ID,
Field_Y,
CASE
WHEN Field_Z IN ('TEST_1','TEST_2') THEN 'CATEG_1'
WHEN Field_Z IN ('TEST_3','TEST_4') THEN 'CATEG_2'
WHEN Field_Z IN ('TEST_5','TEST_6') THEN 'CATEG_3'
ELSE 'CATEG_4'
END AS CATEG
FROM B
INNER JOIN TMP_A
ON TMP_A.ID=TMP_B.ID),
TMP_C AS (
SELECT DISTINCT
ID,
CATEG
FROM TMP_B
WHERE CATEG='CATEG_1'),
TMP_D AS (
SELECT DISTINCT
ID,
CATEG
FROM TMP_B
WHERE CATEG='CATEG_2' AND ID NOT IN (SELECT ID FROM TMP_C)),
TMP_E AS (
SELECT DISTINCT
ID,
CATEG
FROM TMP_B
WHERE CATEG='CATEG_3'
AND ID NOT IN (SELECT ID FROM TMP_C)
AND ID NOT IN (SELECT ID FROM TMP_D))
SELECT * FROM TMP_C
UNION
SELECT * FROM TMP_D
UNION
SELECT * FROM TMP_E
Many thanks in advance for your help.
First off, select DISTINCT will prevent duplicates from the result set, so you are overworking the condition. By adding the "WITH" definitions and trying to nest their use makes it more confusing to follow. The data is ultimately all coming from the "B" table where also has key match in "A". Lets start with just that... And since you are not using anything from the (B)Field_Y or (A)Field_X in your result set, don't add them to the mix of confusion.
SELECT DISTINCT
B.ID,
CASE WHEN B.Field_Z IN ('TEST_1','TEST_2') THEN 'CATEG_1'
WHEN B.Field_Z IN ('TEST_3','TEST_4') THEN 'CATEG_2'
WHEN B.Field_Z IN ('TEST_5','TEST_6') THEN 'CATEG_3'
ELSE 'CATEG_4'
END AS CATEG
FROM
B JOIN A ON B.ID = A.ID
WHERE
B.Field_Z IN ( 'TEST_1', 'TEST_2', 'TEST_3', 'TEST_4', 'TEST_5', 'TEST_6' )
The where clause will only include those category qualifying values you want and still have the results per each category.
Now, if you actually needed other values from your "Field_Y" or "Field_X", then that would generate a different query. However, your Tmp_C, Tmp_D and Tmp_E are only asking for the ID and CATEG columns anyhow.
This may perform better
SELECT DISTINCT B.ID, 'CATEG_1'
FROM
B JOIN A ON B.ID = A.ID
WHERE
B.Field_Z IN ( 'TEST_1', 'TEST_2')
UNION
SELECT DISTINCT B.ID, 'CATEG_2'
FROM
B JOIN A ON B.ID = A.ID
WHERE
B.Field_Z IN ( 'TEST_3', 'TEST_4')
...

Join subquery with min

I'm pulling my hair out over a subquery that I'm using to avoid about 100 duplicates (out of about 40k records). The records that are duplicated are showing up because they have 2 dates in h2.datecreated for a valid reason, so I can't just scrub the data.
I'm trying to get only the earliest date to return. The first subquery (that starts with "select distinct address_id", with the MIN) works fine on it's own...no duplicates are returned. So it would seem that the left join (or just plain join...I've tried that too) couldn't possibly see the second h2.datecreated, since it doesn't even show up in the subquery. But when I run the whole query, it's returning 2 values for some ipc.mfgid's, one with the h2.datecreated that I want, and the other one that I don't want.
I know it's got to be something really simple, or something that just isn't possible. It really seems like it should work! This is MSSQL. Thanks!
select distinct ipc.mfgid as IPC, h2.datecreated,
case when ad.Address is null
then ad.buildingname end as Address, cast(trace.name as varchar)
+ '-' + cast(trace.Number as varchar) as ONT,
c.ACCOUNT_Id,
case when h.datecreated is not null then h.datecreated
else h2.datecreated end as Install
from equipmentjoin as ipc
left join historyjoin as h on ipc.id = h.EQUIPMENT_Id
and h.type like 'add'
left join circuitjoin as c on ipc.ADDRESS_Id = c.ADDRESS_Id
and c.GRADE_Code like '%hpna%'
join (select distinct address_id, equipment_id,
min(datecreated) as datecreated, comment
from history where comment like 'MAC: 5%' group by equipment_id, address_id, comment)
as h2 on c.address_id = h2.address_id
left join (select car.id, infport.name, carport.number, car.PCIRCUITGROUP_Id
from circuit as car (NOLOCK)
join port as carport (NOLOCK) on car.id = carport.CIRCUIT_Id
and carport.name like 'lead%'
and car.GRADE_Id = 29
join circuit as inf (NOLOCK) on car.CCIRCUITGROUP_Id = inf.PCIRCUITGROUP_Id
join port as infport (NOLOCK) on inf.id = infport.CIRCUIT_Id
and infport.name like '%olt%' )
as trace on c.ccircuitgroup_id = trace.pcircuitgroup_id
join addressjoin as ad (NOLOCK) on ipc.address_id = ad.id
The typical approach to only getting the lowest row is one of the following. You didn't bother to specify what version of SQL Server you're using, what you want to do with ties, and I have little interest to try to work this into your complex query, so I'll show you an abstract simplification for different versions.
SQL Server 2000
SELECT x.grouping_column, x.min_column, x.other_columns ...
FROM dbo.foo AS x
INNER JOIN
(
SELECT grouping_column, min_column = MIN(min_column)
FROM dbo.foo GROUP BY grouping_column
) AS y
ON x.grouping_column = y.grouping_column
AND x.min_column = y.min_column;
SQL Server 2005+
;WITH x AS
(
SELECT grouping_column, min_column, other_columns,
rn = ROW_NUMBER() OVER (ORDER BY min_column)
FROM dbo.foo
)
SELECT grouping_column, min_column, other_columns
FROM x
WHERE rn = 1;
This subqery:
select distinct address_id, equipment_id,
min(datecreated) as datecreated, comment
from history where comment like 'MAC: 5%' group by equipment_id, address_id, comment
Probably will return multiple rows because the comment is not guaranteed to be the same.
Try this instead:
CROSS APPLY (
SELECT TOP 1 H2.DateCreated, H2.Comment -- H2.Equipment_id wasn't used
FROM History H2
WHERE
H2.Comment LIKE 'MAC: 5%'
AND C.Address_ID = H2.Address_ID
ORDER BY DateCreated
) H2
Switch that to OUTER APPLY in case you want rows that don't have a matching desired history entry.

Resources