LINQ - Query with distinct/GroupBy on multiple properties problem - entity-framework

I have some data stored in an EntitySet of my domainContext where I want to select out just parts of it. Problem is that I'm getting all the CageNames - and not just the "New" ones with the highest date.
This is the data:
SiteId CageId CageName Date
1 ,1 ,Lot1 ,'2011-05-05'
1 ,1 ,LotNew ,'2011-05-06'
1 ,2 ,Lot1 ,'2011-05-05'
1 ,2 ,LotNew ,'2011-05-06'
1 ,3 ,Lot1 ,'2011-05-05'
1 ,3 ,LotNew ,'2011-05-06'
So for each CageId listed I want to return the CageName on the record that has the highest date.
My code trying to do this is here:
Dim CageName As Array
CageName = DomainContext.SiteCageDatas.Where(Function(h) h.SiteId = SiteId) _
.Select(Function(x) x.CageName).Distinct.ToArray()
But this just gives me all CageNames.. Any idea how I should fix this, I guess maybe using groupby or something?
Mind; I'm using VB.NET

I'm doing this now, but that returns nothing :(
CageName = DomainContext.SiteCageDatas.GroupBy(Function(a) New With {a.SiteId, a.CageId}).Select(Function(a) a.OrderBy(Function(b) b.Date).LastOrDefault()).Where(Function(x) x.SiteId = SiteId).Select(Function(x) x.CageName).ToArray()

Try this:
DomainContext.SiteCageDatas.GroupBy(Function(x) x.CageId).Select(Function(x) x.OrderBy(Function(x) x.Date).LastOrDefault()).Where(Function(x) x IsNot Nothing).Select(Function(x) x.CageName).ToArray()

Related

mismatched input 'as'. Expecting: ',', <expression>

My query in PRESTO returns this error
Query failed (#20220506_153121_03035_vycq3): line 6:41: mismatched
input 'as'. Expecting: ',',
I don't know why, anybody could find the issue?
select
droh.created_by as deliverymen_email,
count(distinct o.order_number) as deliveries,
sum(case when o.last_status = 10 then 1 else 0 end) quantity_canceled,
cast(sum(quantity_canceled as decimal))/cast(count(deliveries as decimal)) as
delivery_cancellation_fee
from sensitive_raw_courier_api.deliveryman_route_order_history droh
left join raw_courier_api."order" o
on droh.order_number = o.order_number and droh.state = 'DM_PICKED_UP'
where 1=1
and o.created_date >= {{date_begin}}
and droh.created_at >= {{date_end}}
and o.customer_email = {{costumer_email}}
group by 1
order by 2 desc
There is an error with the position of 2 brackets.
We count(~) first and then cast( count(~) ) the result.
cast(sum(quantity_canceled as decimal))/cast(count(deliveries as decimal)) as
should be
cast(sum(quantity_canceled) as decimal)/cast(count(deliveries) as decimal) as
Without the context and further information, it's not sure if this is your only issue in this query, but you can't "mix" a cast with a sum or a count like you do. You need to do the cast first and then sum or count the values (or vice versa). So as example this syntax in your query is incorrect:
CAST(SUM(quantity_canceled AS DECIMAL))
It should be this one instead:
SUM(CAST(quantity_canceled AS DECIMAL))
Or this one:
CAST(SUM(quantity_canceled) AS DECIMAL)
You must fix all occurences of this mistake and then check if the query is correct or contains further problems.
A last note: Doing a division could always end in a division by zero exception if you don't prevent this. So you should take care of this.

selecting daily revised hourly values for a month

I have two queries
first one return latest Revision number for each date for a month
select max(a.wdcm_revision_no)
from wb_declared_capacity_master a, wb_declared_capacity_det_unit b
where a.wdcm_internal_id = b.wdcd_ref_id and b.wdcm_unit = 'HSY_Unit1' and to_char(a.wdcm_date,'MM YYYY')='08 2019' group by a.wdcm_date
And second query which returns all the data irrespective of any revision
select a1.wdcm_date, b1.wdcd_block_no, c1.wdcd_capacity, c1.wdcd_approval, a1.wdcm_revision_no
from wb_declared_capacity_master a1, wb_declared_capacity_det_unit b1, wb_declared_capacity_detail c1
where a1.wdcm_internal_id = b1.wdcd_ref_id and a1.wdcm_internal_id = c1.wdcd_ref_id and b1.wdcm_unit = 'HSY_Unit1' and to_char(a1.wdcm_date,'MM YYYY')='08 2019'and b1.wdcd_block_no = c1.wdcd_block_no
Now I want to pass each date's latest revision from first query in where condition of second query expecting to return all the values based on those latest revision number
You can combine then into 1 query. The 1st becoming a CTE which is then fed into the 2nd. I believe the following is close, but as you didn't actually indicate where you wanted to "pass in the result" I'm making assumption that seems likely, but it's still an assumption.
with latest (revision) as
-- first query
( select max(a.wdcm_revision_no)
from wb_declared_capacity_master a
, wb_declared_capacity_det_unit b
where a.wdcm_internal_id = b.wdcd_ref_id
and b.wdcm_unit = 'HSY_Unit1'
and to_char(a.wdcm_date,'MM YYYY')='08 2019'
group by a.wdcm_date
)
-- And second query which returns all the data irrespective of any revision
select a1.wdcm_date
, b1.wdcd_block_no
, c1.wdcd_capacity
, c1.wdcd_approval
, a1.wdcm_revision_no
from wb_declared_capacity_master a1
, wb_declared_capacity_det_unit b1
, wb_declared_capacity_detail c1
, latest
where a1.wdcm_internal_id = b1.wdcd_ref_id
and a1.wdcm_internal_id = c1.wdcd_ref_id
and b1.wdcm_unit = 'HSY_Unit1'
and to_char(a1.wdcm_date,'MM YYYY')='08 2019'
and b1.wdcd_block_no = c1.wdcd_block_no
and a1.wdcm_revision_no = latest.revision; ---- you didn't actually say where to "pass it in" this seems likely
I've kept the join format you used but you really should begin using the modern ANSI/ISO standard join format. (Modern meaning being in ONLY since 1992 or so).

Exclude where clause based on function specification

I developed the following function:
create function kv_fn_ValuationPerItem_AW (#dDate date, #active bit)
returns table
as
return
(
select
Code ItemCode
, Description_1 ItemDescription
, ItemGroup
, Qty_On_Hand CurrentQtyOnHand
, AveUCst CurrentAvgCost
, Qty_On_Hand*AveUCst CurrentValue
from _bvSTTransactionsFull t
inner join StkItem s on t.AccountLink = s.StockLink
where ServiceItem = 0
and ItemActive = #active
and TxDate <= #dDate
group by Code, Description_1, ItemGroup, Qty_On_Hand, AveUCst
)
The function requires two parameters:
Date
Is the item Active - 1 = Active & 0 = Inactive
If I use the function as stipulated above, by specifying 1 for the Active Parameter, then the results will only be for Active Items.
If I specify 0, then it'll return all inactive Items.
How do I alter this function to cater for Active Items or both Active & Inactive?
i.e. if the parameter is 1, the where clause should read as ItemActive = #active, but when it's 0, the where clause should read as ItemActive in (1,0), How do I change the function to work like this?
I tried a case, but my syntax is not correct...
It's as simple as adding an or to your where cluase:
...
and (ItemActive = 1 OR #active = 0)
...
BTW, you might want to do it like this instead:
and (ItemActive = #active OR #active IS NULL)
which means that when you pass in 1 as #active you'll get only the active items, when you pass in 0 you'll get only the inactive members, but when you pass in null you'll get all records, regardless of the value in the ItemActive column.
Thanks Shnugo & Zohar for your answers,
Please amend your answers, then I'll mark yours as the answer.
The solution to my problem was to alter the Function as following:
create function kv_fn_ValuationPerItem_AW (#dDate date, #active bit)
returns table
as
return
(
select
Code ItemCode
, Description_1 ItemDescription
, ItemGroup
, Qty_On_Hand CurrentQtyOnHand
, AveUCst CurrentAvgCost
, Qty_On_Hand*AveUCst CurrentValue
from _bvSTTransactionsFull t
inner join StkItem s on t.AccountLink = s.StockLink
where ServiceItem = 0
and ItemActive in (1,#active)
and TxDate <= #dDate
group by Code, Description_1, ItemGroup, Qty_On_Hand, AveUCst
)
I think you are looking for this:
DECLARE #mockup TABLE(ID INT IDENTITY,SomeValue VARCHAR(100),Active BIT);
INSERT INTO #mockup VALUES('Row 1 is active',1)
,('Row 2 is active',1)
,('Row 3 is inactive',0)
,('Row 4 is inactive',0);
DECLARE #OnlyActive BIT=0; --set this to 1 to see active rows only
SELECT *
FROM #mockup m
WHERE (#OnlyActive=0 OR m.Active=1);
The idea is: If the parameter is set to 0 this expression is always true, if not, the column Active must be set to 1.
Hint: I used paranthesis, which was not needed in this simple case. But in your more complex WHERE clause they will be needed...
Hint2: I named the parameter OnlyActive, which expresses a bit better what you are looking for. You might turn the parameter to ShowAll with an invers logic too...

Merge rows with connecting dates

I've got a large Excel sheet with customer and subscription data. From this table I would like to merge records/rows with connection stop_ and start_dates and show the result in a new worksheet. A simplified version of the data is shown below.
Customer_id subscription_id start_date stop_date
1034 RV4 30-4-2012 30-1-2015
1035 AB7 30-1-2014 30-3-2014
1035 AB6 30-1-2014 30-3-2014
1035 AB7 30-12-2013 30-1-2014
1035 AB7 12-12-2012 30-12-2013
1035 AB7 12-9-2010 14-1-2011
So, the formula has to check the customer_id and the subscription_id. When there is a match between two or more rows in the sheet and the stop_date of one of the rows overlaps with the start_date of the other row, then after the extraction and merging, one new row must be shown with the start_date of the first and the stop_date of the other row. This also has to work if there are multiple rows with connecting dates. All the rows that don't match these criteria stay the same after the extraction. So the result will be like this:
Customer_id subscription_id start_date stop_date
1034 RV4 30-4-2012 30-1-2015
1035 AB6 30-1-2014 30-3-2014
1035 AB7 12-12-2012 30-3-2014
1035 AB7 12-9-2010 14-1-2011
A dynamic solution would be ideal while new data will be added to the original sheet. While I know this is possible when you're certain that the rows you're looking for are always below each other, this is not the case here and it wouldn't give you a very dynamic solution.
So some kind of array function would be needed in Excel I guess but after searching a lot I couldn't find a suitable solution. I've also got MATLAB available but no clue where to start in that program with a problem like this.
A dynamic solution may be possible, but if the dataset it large it might bog things down quite a bit because you'd need it to run every time a cell was changed.
Basically the best way I can see to approach this is to create unique keys out your customer_id and subscription_id, then collect all of the date ranges under that key and merge them.
Something like this should get you started (requires a reference to Microsoft Scripting Runtime):
Public Sub LinkSubscriptionDates()
Dim data As Dictionary, source As Worksheet, target As Worksheet
Set source = ActiveSheet
Set data = GetSubscriptions(source)
Set target = source.Parent.Worksheets.Add
'Copy headers
target.Range(target.Cells(1, 1), target.Cells(1, 4)).Value = _
source.Range(source.Cells(1, 1), source.Cells(1, 4)).Value
Dim row As Long
row = 2
Dim key As Variant, item As Variant
For Each key In data.Keys
For Each item In data(key)
target.Cells(row, 1) = Split(key, "|")(0)
target.Cells(row, 2) = Split(key, "|")(1)
target.Cells(row, 3) = Split(item, "|")(0)
target.Cells(row, 4) = Split(item, "|")(1)
row = row + 1
Next item
Next key
End Sub
Private Function GetSubscriptions(source As Worksheet) As Dictionary
Dim subscrips As Dictionary
Set subscrips = New Dictionary
Dim row As Long
Dim cust As String, subs As String, starting As String, ending As String
'Gather all the data as pairs of customer|subscription, starting|ending
For row = 2 To source.UsedRange.Rows.Count
Dim dates() As String
cust = source.Cells(row, 1).Value
subs = source.Cells(row, 2).Value
'Valid customer/subscription?
If cust <> vbNullString And subs <> vbNullString Then
starting = source.Cells(row, 3).Value
ending = source.Cells(row, 4).Value
'Has an ending and starting date?
If starting <> vbNullString And ending <> vbNullString Then
Dim key As String
key = cust & "|" & subs
'New combo?
If Not subscrips.Exists(key) Then
subscrips.Add key, New Collection
subscrips(key).Add starting & "|" & ending
Else
subscrips(key).Add starting & "|" & ending
Set subscrips(key) = MergeDates(subscrips(key))
End If
End If
End If
Next row
Set GetSubscriptions = subscrips
End Function
Private Function MergeDates(dates As Collection) As Collection
Dim candidate As Long, index As Long
Dim values() As String, test() As String
Dim merge As Boolean
For index = 1 To dates.Count
values = Split(dates(index), "|")
'Check to see if it can be merged with any other row.
For candidate = index + 1 To dates.Count
test = Split(dates(candidate), "|")
If CDate(test(0)) >= CDate(values(0)) And _
CDate(test(0)) <= CDate(values(1)) Or _
CDate(test(1)) >= CDate(values(0)) And _
CDate(test(1)) <= CDate(values(1)) Then
dates.Remove candidate
merge = True
Exit For
End If
Next candidate
If merge Then Exit For
Next index
If merge Then
'Pull both rows out of the collection.
dates.Remove index
values(0) = IIf(CDate(test(0)) < CDate(values(0)), _
CDate(test(0)), CDate(values(0)))
values(1) = IIf(CDate(test(1)) > CDate(values(1)), _
CDate(test(1)), CDate(values(1)))
'Put the merged date range back in.
dates.Add values(0) & "|" & values(1)
'Recurse.
Set MergeDates = MergeDates(dates)
End If
Set MergeDates = dates
End Function
It really needs to be fleshed out with data validation, error trapping, etc., and it currently just puts the resulting data on a new worksheet. All the work gets done in the GetSubscriptions function, so you can just grab returned Dictionary from that and do whatever you need to do with that data in it.

Pulling correct results from my PitchValues Table

I am getting a tad frustrated and was wondering if you can help:
I have a Pitch Values Table with the following Columns PitchValues_Skey, PitchType_Skey (this is a foreign key), Start Date, End Date and finally value:
For Example:
1 7 01/01/2010 31/12/2010 £15
2 7 01/01/2011 31/12/2011 £20
And all I want to do is update my Bookings table with how much each booking is going to be, so I put together the code below which worked fine when I only had 2010 data, but I know have 2011 and 2012 and want to update it but it will only update with the 2010 prices.
SELECT Bookings.Booking_Skey, DATEDIFF(day, Bookings.ArrivalDate, Bookings.DepartureDate) * PitchValues.Value AS BookingValue,
PitchValues.PitchType_Skey
FROM Bookings INNER JOIN
PitchValues ON Bookings.PitchType_Skey = PitchValues.PitchType_Skey
WHERE (Bookings.Booking_Skey = 1)
So when I run the query above I would expect to see one line of data but instead I see 4 (See Below)
I would expect this:
Booking_Skey BookingValue PitchType_Skey
1 420 4
But I get this
Booking_Skey BookingValue PitchType_Skey
1 420 4
1 453.6 4
1 476.7 4
1 476.7 4
All sorted now, thanks for your help.
SELECT Bookings.Booking_Skey, DATEDIFF(DAY, Bookings.ArrivalDate, Bookings.DepartureDate) * PitchValues.Value AS BookingValue, PitchValues.PitchType_Skey
FROM Bookings
INNER JOIN PitchValues ON Bookings.PitchType_Skey = PitchValues.PitchType_Skey
AND Bookings.ArrivalDate BETWEEN PitchValues.StartDate AND PitchValues.EndDate
WHERE (Bookings.Booking_Skey = 1)