SQL Server : calculated column for parent/child level - sql-server

Having some issues displaying a calculated column.
I'm displaying a line for parent, then a few (anywhere between 1-5) lines for each child item.
My first query is this:
Select
_r1 = pbhier.r1
, pb.parent_id
, pb.Product_id
, pb.LastCost
, pb.AverageCost
, pb.FirstDatePurchased
, lrd.LastReceivingDate
, inv_units = isnull(inv.quantity,0)
, sales_units = convert(int,isnull(sales.SalesUnits,0))
, number_months_supply = case when isnull(sales.salesUnits,0) = 0 then isnull(inv.quantity,0)
else
case when (isnull(sales.salesUnits,0)/12) = 0 then 0
else
(convert(int,(inv.quantity/(isnull(sales.salesUnits,0)/12)))) end end
, inv.quantity, sales.SalesUnits
, ext_AverageCost = isnull(inv.quantity,0) * pb.averagecost
INTO #tempdata
From Products pb
inner join #TempFinalHier pbhier on pb.product_id = pbhier.product_id
left join #inventoryPC inv (nolock) on pb.product_id = inv.product_id
left join #tempTotSalesPC sales (nolock) on pb.product_id = sales.product_id
left join #lastReceivingDate lrd on lrd.rm_product_id = pb.product_id
where pb.Parent_ID in (select parent_id from #FinalParentSelection)
This creates a temp data set. The column "number_months_supply" does not need to be calculated above but the same formula should be used in my final query. My final query below gets me the final result set:
Select distinct
H.R1 , H.R2, H.Lvl,
case
when R1 <> R2
then H.parent_sku
else H.SKU
end as SKU,
H.parent_sku,
FirstReceiptDate = min(B.FirstDatePurchased),
LastReceiptDate = max(B.LastReceivingDate),
OnHand = sum(B.inv_units),
SalesUnits = sum(B.sales_units),
case
when isnull(sum(B.sales_units),0) = 0
then isnull(sum(B.inv_units),0)
else
case
when (isnull(sum(B.sales_units),0)/12) = 0
then 0
else convert(decimal(10, 2), (sum(B.inv_units) / (isnull(sum(B.sales_units), 0) / 12)))
end
end,
max(B.ext_AverageCost) as ext_AverageCost
From
#TempFinalHier H
join
#TempData1 B on (B._R1 between H.R1 and H.R2)
-- where H.Lvl=1 --Show only parent line
Group By
H.R1, H.R2, H.Lvl, H.parent_sku
having
sum(B.number_months_supply) > 12 --#MonthsSupplyGreaterThanOrEqual
Order by
R1
What I'm trying to get is:
'onHand' for parent line (lvl1) needs to be sum of all child rows (lvl2). same applies to 'SalesUnits'. Finally, Number_months_supply formula is (inv/sales_units)*12. I would like to display the number_months_supply as numeric with 2 decimal points.
Would appreciate some help with this.
Thanks.

Related

EF LINQ Count by Grouped field

I have the following data schema:
With the following LINQ query:
var profiles = (
from p in context.BusinessProfiles
join u in context.Users on p.UserId equals u.Id
join addr in context.BusinessAddress on p.ProfileId equals addr.ProfileId into addrj
from addr in addrj.DefaultIfEmpty()
join pa in context.BusinessProfileActivities on p.ProfileId equals pa.ProfileId into paj
from paIfNull in paj.DefaultIfEmpty()
where p.ProfileId >= 137 && p.ProfileId <= 139
group new { p, u, addr, paIfNull }
by new {
p.ProfileId,
p.CompanyName,
p.Email,
UserEmail = u.Email,
addr.City, addr.Region,
addr.Country,
ActivityProfileId = paIfNull.ProfileId }
into pg
select new {
pg.Key.ProfileId,
pg.Key.CompanyName,
Email = pg.Key.Email ?? pg.Key.UserEmail,
pg.Key.City,
pg.Key.Region,
pg.Key.Country,
MatchingActivities = pg.Key.ActivityProfileId > 0 ? pg.Count() : 0
} into result
orderby result.MatchingActivities descending
select result
);
Which results with:
This result is corrent (ProfileId 137 has 0 activities, 138 has 1 and 139 has 2), but it produces the following SQL:
SELECT [b].[ProfileId], [b].[CompanyName], COALESCE([b].[Email], [a].[Email]) AS [Email], [b0].[City], [b0].[Region], [b0].[Country],
CASE WHEN [b1].[ProfileId] > CAST(0 AS bigint) THEN COUNT(*)
ELSE 0
END AS [MatchingActivities]
FROM [BusinessProfiles] AS [b]
INNER JOIN [AspNetUsers] AS [a] ON [b].[UserId] = [a].[Id]
LEFT JOIN [BusinessAddress] AS [b0] ON [b].[ProfileId] = [b0].[ProfileId]
LEFT JOIN [BusinessProfileActivities] AS [b1] ON [b].[ProfileId] = [b1].[ProfileId]
WHERE ([b].[ProfileId] >= CAST(137 AS bigint)) AND ([b].[ProfileId] <= CAST(139 AS bigint))
GROUP BY [b].[ProfileId], [b].[CompanyName], [b].[Email], [a].[Email], [b0].[City], [b0].[Region], [b0].[Country], [b1].[ProfileId]
ORDER BY CASE
WHEN [b1].[ProfileId] > CAST(0 AS bigint) THEN COUNT(*)
ELSE 0
END DESC
In SQL, I can avoid both CASE WHEN if I use COUNT([b1].[ProfileId]) like this:
SELECT [b].[ProfileId], [b].[CompanyName], COALESCE([b].[Email], [a].[Email]) AS [Email], [b0].[City], [b0].[Region], [b0].[Country],
COUNT([b1].[ProfileId]) AS [MatchingActivities]
FROM [BusinessProfiles] AS [b]
INNER JOIN [AspNetUsers] AS [a] ON [b].[UserId] = [a].[Id]
LEFT JOIN [BusinessAddress] AS [b0] ON [b].[ProfileId] = [b0].[ProfileId]
LEFT JOIN [BusinessProfileActivities] AS [b1] ON [b].[ProfileId] = [b1].[ProfileId]
WHERE ([b].[ProfileId] >= CAST(137 AS bigint)) AND ([b].[ProfileId] <= CAST(139 AS bigint))
GROUP BY [b].[ProfileId], [b].[CompanyName], [b].[Email], [a].[Email], [b0].[City], [b0].[Region], [b0].[Country], [b1].[ProfileId]
ORDER BY [MatchingActivities] DESC
My question is, how can I count by grouped ActivityProfileId = paIfNull.ProfileId using LINQ and get EF to generate the above SQL?
I have tried so many variations resulting mostly in EF to SQL errors.
MatchingActivities = pg.Count(t => t.ActivityProfileId!= 0)
MatchingActivities = pg.Select(t => t.paIfNull.ProfileId).Distinct().Count(),
MatchingActivities = pg.Count(t => t.paIfNull != null),
All result in errors like System.InvalidOperationException: The LINQ expression ... could not be translated. or getting MatchingActivities as 1 instead of 0.
Related Q/A:
LINQ Count returning 1 instead of zero for an empty group
Group by in LINQ
How to write left join, group by and average in c# entity framework Linq
In short you can't! EF Core still doesn't support that.
See this:
https://github.com/dotnet/efcore/issues/17376
And also See:
https://stackoverflow.com/a/61878332/9212040

Aggregate function is return incorrect value when joining more table

I am getting the Total sale using the following query.
SELECT SUM([B].[TotalSale])
FROM [dbo].[BookingDetail] [BF] WITH (READPAST)
INNER JOIN [dbo].[Booking] [B] WITH (READPAST) ON [B].[BookingDetailID] = [BF].[ID]
WHERE [BF].[MarketID] = '2'
I want to add another column to get the Gross Sale .
For that I have to make a join with another table called AirTraveler.
But once I add a new table to the query
SELECT
SUM([B].[TotalSale]) ,
SUM(CASE WHEN [B].[TravelSectorID] = 3 AND [B].[BookingStatusID] IN (16, 20, 22, 23) THEN COALESCE([B].[TotalSale], 0.0)
WHEN ([B].[TravelSectorID] = 1 AND [B].[IsDomestic] = 1 AND CONVERT(varchar, [AT].[FareDetails].query('string(/AirFareInfo[1]/PT[1])')) = 'FlightAndHotel') THEN [AT].[TotalSale]
ELSE 0 END) AS [GrossSale]
FROM [dbo].[BookingDetail] [BF] WITH (READPAST)
INNER JOIN [dbo].[Booking] [B] WITH (READPAST) ON [B].[BookingDetailID] = [BF].[ID]
LEFT OUTER JOIN [dbo].[AirTraveler] [AT] WITH(READPAST) ON [B].[ID] = [AT].[BookingID]
WHERE [BF].[MarketID] = '2'
it is giving incorrect result of [TotalSale] .the aggregate functions return wrong values because there may be multiple AirTraveler per Booking ID, which is correct. What can I do to solve the aggregate function problem?
I am actually stuck.
I am using SQL Server .
Thanks in advance.
Not tested or anything, but when you are joining to a lower level table that causes a header table to double count, you can pre-aggregate it before it joins
This is probably missing some opening/closing brackets and aliases but hopefully you can work it out
SELECT
SUM([B].[TotalSale]) ,
SUM(CASE WHEN [B].[TravelSectorID] = 3
AND [B].[BookingStatusID] IN (16, 20, 22, 23)
THEN COALESCE([B].[TotalSale], 0.0)
WHEN ([B].[TravelSectorID] = 1 AND [B].[IsDomestic] = 1
THEN [AT].[TotalSale]
ELSE 0 END) AS [GrossSale]
FROM [dbo].[BookingDetail] [BF] WITH (READPAST)
INNER JOIN [dbo].[Booking] [B] WITH (READPAST) ON [B].[BookingDetailID] = [BF].[ID]
LEFT OUTER JOIN
(
SELECT BookingID, SUM(CASE WHEN
CONVERT(varchar(50), [FareDetails].query('string(/AirFareInfo[1]/PT[1])'))
= 'FlightAndHotel') THEN [TotalSale] ELSE 0 END) TotalSale
FROM [dbo].[AirTraveler] [AT] WITH(READPAST)
GROUP BY BookingID
) AT
ON [B].[ID] = [AT].[BookingID]
WHERE [BF].[MarketID] = '2'
Also I gave your varchar cast a size - I think if you don't do this it'll be 1 so your case is never true

Duplicate fields in a table, distinct doesn't work

I have the following query:
select distinct
ROW_ID = row_number() over (
order by wms_us.wms_us.rrno.rrdate
, wms_us.wms_us.rrno.pono
, wms_us.wms_us.transferboxdet.meidhex
, att.Date_cleared
)
, ATT_PO = wms_us.wms_us.rrno.pono
, Received_Date = wms_us.wms_us.rrno.rrdate
, IMEI = case
when len(wms_us.wms_us.transferboxdet.meidhex) >= 15
then left(wms_us.wms_us.transferboxdet.meidhex, 14)
else wms_us.wms_us.transferboxdet.meidhex
end
, Model = case
when (wms_us.wms_us.model.modeldesc = 'MIXED')
then wms_us.wms_us.transferboxdet.basemodel
else wms_us.wms_us.model.modelbase
end
, Date_cleared = case
when (Future.[Error Code] = '1')
then Future.LocalTime
else att.Date_cleared
end
, Result = case
when (Future.[Error Code] = '1')
then 'PASS'
else att.Result
end
from wms_us.wms_us.transferboxdoc
inner join wms_us.wms_us.transferboxdet
on wms_us.wms_us.transferboxdoc.transferboxdocid
= wms_us.wms_us.transferboxdet.transferboxdocid
inner join wms_us.wms_us.rrno
on wms_us.wms_us.transferboxdet.rrnoid = wms_us.wms_us.rrno.rrnoid
inner join wms_us.wms_us.model
on wms_us.wms_us.transferboxdoc.modelid = wms_us.wms_us.model.modelid
left join DRSCSQLQADB01.att_view2.dbo.attview2 as att
on att.IMEI = LEFT(wms_us.wms_us.transferboxdet.meidhex, 14)
inner join Futerdial.dbo.Futuredial_Validation as Future
on Future.IMEI = wms_us.wms_us.transferboxdet.meidhex
where (wms_us.wms_us.rrno.rrdate > '2016-12-01')
and Future.IMEI = '352130070643357'
I have the IMEI in the table Futuredial_Validation duplicate, I have been trying to use the Distinct just to show me one IMEI but it's not working, there is any alternative that I can use?? Or I am using it wrong?
Attach a picture of what is showing me, I just one be able to see one the first one.
Screenshot
Thanks
For rows to be filtered out by distinct, all values must be the same. In this query it looks like you can just wrap the query in a CTE and filter on ROW_ID = 1.

How to supply multiple values in between clause after where clause

SELECT
ROW_NUMBER() OVER (ORDER BY Vendor_PrimaryInfo.Vendor_ID ASC) AS RowNumber,
*
FROM
Unit_Table
INNER JOIN
Vendor_Base_Price ON Unit_Table.Unit_ID = Vendor_Base_Price.Unit_ID
INNER JOIN
Vendor_PrimaryInfo ON Vendor_Base_Price.Vendor_ID = Vendor_PrimaryInfo.Vendor_ID
INNER JOIN
Vendor_Registration ON Vendor_Base_Price.Vendor_ID = Vendor_Registration.Vendor_ID
AND Vendor_PrimaryInfo.Vendor_ID = Vendor_Registration.Vendor_ID
INNER JOIN
Category_Table ON Vendor_Registration.Category_ID = Category_Table.Category_ID
LEFT JOIN
Vendor_Value_Table ON Vendor_Registration.Vendor_ID = Vendor_Value_Table.Vendor_ID
LEFT JOIN
Feature_Table ON Vendor_Value_Table.Feature_ID = Feature_Table.Feature_ID
WHERE
Vendor_Registration.Category_ID = 5
AND Vendor_PrimaryInfo.City = 'City'
AND (value_text in ('sample value') or
(SELECT
CASE WHEN ISNUMERIC(value_text) = 1
THEN CAST(value_text AS INT)
ELSE -1
END) BETWEEN 0 AND 100)
As column has multiple values which may be text or may be int that's why I cast based on case. My question is: I just want to fetch the records either whose value is between 0 and 100 or value between 300 to 400 or value is like sample value.
I just want to place the condition after where clause and do not want to use column_name multiple time in between operator because these values are coming from url
Thanks in advance any help would be grateful.
You can try this way..
WHERE
Vendor_Registration.Category_ID = 5
AND Vendor_PrimaryInfo.City = 'City'
AND (value_text in ('sample value') or
(CASE WHEN (ISNUMERIC(value_text) = 1)
THEN CAST(value_text AS INT)
ELSE -1
END) BETWEEN 0 AND 100)

Updating multiple columns for each record from table with multiple records for each ID

This is my first time posting a question here so please be gentle, I searched as exhaustively as I could. Sometimes it's how to search for the answer that is half the battle.
What I'm trying to do is update Table 1 with the data from Table 2 for each person for each period. Some people will have category A,B, and/or C records in Table 2, but not all and not necessarily all three.
The problem I'm running into is that my statement I'm using to do the update will update some of the columns but not all. I'm guessing this is because the updates weren't committed yet while doing the update so it can't fetch values that haven't been committed yet.
Do I need to do 3 separate update statements or can this somehow be handled through a case statement. I'm looking for most efficient methods here. Updating Table 1 who has 2 million records for each period.
Table 1 - Period_Perf
CustID
Period_Date
Perf_Cat_A
Perf_Cat_B
Perf_Cat_C
Table 2 - Period_Perf_Detail
CustID
Period_Date
Perf_Category (will contain A, B, or C)
Perf_Points (will contain a integer value)
Here's essentially the statement I've been trying to use:
UPDATE
Period_Perf
SET
Perf_Cat_A = CASE WHEN pd.Perf_Category = 'A' then pd.Total_Perf_Points else Perf_Cat_A END
Perf_Cat_B = CASE WHEN pd.Perf_Category = 'B' then pd.Total_Perf_Points else Perf_Cat_B END
Perf_Cat_C = CASE WHEN pd.Perf_Category = 'C' then pd.Total_Perf_Points else Perf_Cat_C END
from
Period_Perf
ON
INNER JOIN
(
select
CustID
,Period_Date
,Perf_Category
,sum(Perf_Points) as Total_Perf_Points
from
Period_Perf_Detail
group by CustID, Period_Date, Perf_Category
) as pd
ON
Period_Perf.CustID = pd.CustID and Period_Perf.Period_Date = pd.Period_Date
To update all the values at once, you'll need to have data that has all those three values in one row, so something like this:
UPDATE P
SET
Perf_Cat_A = pd.Cat_A,
Perf_Cat_B = pd.Cat_B,
Perf_Cat_C = pd.Cat_C
from
Period_Perf P
cross apply (
select
sum(case when Perf_Category = 'A' then Perf_Points else 0 end) as Cat_A,
sum(case when Perf_Category = 'B' then Perf_Points else 0 end) as Cat_B,
sum(case when Perf_Category = 'C' then Perf_Points else 0 end) as Cat_C
from
Period_Perf_Detail D
where
D.CustID = P.CustID and
D.Period_Date = P.Period_Date
) as pd
I haven't tested this, but hopefully there's no bugs.
Use SELECT statements first to understand your data, then build an update based on that select statement.
First use the following query to better understand why your update statement is not producing your desired results.
SELECT CASE WHEN pd.Perf_Category = 'A' THEN pd.Total_Perf_Points ELSE Perf_Cat_A END AS Perf_Cat_A
, CASE WHEN pd.Perf_Category = 'B' THEN pd.Total_Perf_Points ELSE Perf_Cat_B END AS Perf_Cat_B
, CASE WHEN pd.Perf_Category = 'C' THEN pd.Total_Perf_Points ELSE Perf_Cat_C END AS Perf_Cat_C
FROM Period_Perf
ON
INNER JOIN
(
SELECT CustID
, Period_Date
, Perf_Category
, SUM(Perf_Points) AS Total_Perf_Points
FROM Period_Perf_Detail
GROUP BY CustID
, Period_Date
, Perf_Category ) AS pd
ON Period_Perf.CustID = pd.CustID
AND Period_Perf.Period_Date = pd.Period_Date
I suggest the following query for your update.
UPDATE Period_Perf
SET Perf_Cat_A = SUM(a.Perf_Points)
, Perf_Cat_B = SUM(b.Perf_Points)
, Perf_Cat_C = SUM(c.Perf_Points)
FROM Period_Perf
LEFT JOIN Period_Perf_Detail AS a
ON Period_Perf.CustID = a.CustID
AND Period_Perf.Period_Date = a.Period_Date
AND a.Perf_Category = 'A'
LEFT JOIN Period_Perf_Detail AS b
ON Period_Perf.CustID = b.CustID
AND Period_Perf.Period_Date = b.Period_Date
AND a.Perf_Category = 'B'
LEFT JOIN Period_Perf_Detail AS c
ON Period_Perf.CustID = c.CustID
AND Period_Perf.Period_Date = c.Period_Date
AND a.Perf_Category = 'C'
GROUP BY Period_Perf.CustID , Period_Perf.Period_Date
Please notice that instead putting criteria in a WHERE, it is in the join itself. This way you don't affect the entire dataset, but just the subsets individually on each join.

Resources