Use linq in the where clause - asp.net

I have 2 tables _customerRepository.GetAllQueryable() and _customerSettlementRepository.GetAllQueryable().
In table _customerSettlementRepository.GetAllQueryable(), I have column ApplyD (date), after joining these two together, I want to find out max ApplyD in the where clause. This is my code:
var settlements = from c in _customerRepository.GetAllQueryable()
join cs in _customerSettlementRepository.GetAllQueryable() on new {c.CustomerMainC, c.CustomerSubC}
equals new {cs.CustomerMainC, cs.CustomerSubC} into c1
where cs.ApplyD == (c1.Select(b=>b.ApplyD).Max())
select new CustomerSettlementViewModel()
{
TaxRate = cs.TaxRate
};

It's remarkable that quite often in these questions people come up with an SQL(-like) statement without specification of the goal they want to reach. Hence it is impossible to see whether the provided statement fulfills the requirements.
Anyway, it seems you have something like Customers (in CustomerRepository) and CustomerSettlements in CustomerSettlementRepository.
both Customers and CustomerSettlements have a CustomerMainC and a CustomerSubC. You want to join Customers and CustomerSettlements on these two properties.
A CustomerSettlement also has an ApplyD and a TaxRate.
You only want to keep the join results where ApplyD has the maximum value of ApplyD
Finally, from every remaining join result you want to create one CustomerSettlementViewModel object with the value of the TaxRate in the join result that was taken from the CustomerSettlement.
Now that I wrote this, it baffles me why you need to join in the first place, because you only use values from the CustomerSettlements, not from the Customer.
Besides, if two Customers are joined with the same CustomerSettlements. this will result in two equal CustomerSettlementViewModel objects.
But let's assume this is really what you want.
In baby steps:
IQueryable<Customer> customers = ...
IQueryable<CustomerSettlement> customerSettlements = ...
var joinResults = customers.Join(customerSettlements
customer => new {customer.CustomerMainC, customer.CustomerSubC},
settlement => new {settlement.CustomerMainC, settlement.CustomerSubC}
(customer, settlement) => new
{
settlement.ApplyD,
settlement.TaxRate,
// add other properties from customers and settlements you want in the end result
});
In words: take all Customers and all CustomerSettlements. From every Customer create an object having the values of the customer's CustomerMainC and CustomerSubC. Do the same from every CustomerSettlement. When these two objects are equal, create a new object, having the values of the CustomerSettlement's ApplyD and TaxRate (and other properties you need in the end result)
Note that this is still an IQueryable. No query is performed yet.
From this joinResult you only want to keep those objects that have the value of ApplyD that equals the maximum value of ApplyD.
This question on StackOverflow is about selecting the records with the max value. The idea is to group the records into groups with the same value for ApplyD. Then order the groups in descending Key order and take the first group.
var groupsWithSameApplyD = joinResults.GroupBy(
joinedItem => joinedItem.ApplyD,
joinedItem => new CustomerSettlementViewModel()
{
TaxRate = orderedItem.TaxRate,
// add other values from joinedItems as needed
});
Every group in groupsWithSameApplyD has a key equal to ApplyD. The group consists of CustomerSettlementViewModel objects created frome the joinedItems that all have the same ApplyD that is in the Key of the group.
Now order by descending:
var orderedGroups = groupsWithSameApplyD.OrderByDescending(group => group.Key);
The first group contains all elements that had the largest ApplyD. Your desired result is the sequence of elements in the group.
If there is no group at all, return an empty sequence. Note if a sequence is requested as result, it is always better to return an empty sequence instead of null, so callers can use the returned value in a foreach without having to check for null return
var result = orderedGroups.FirstOrDefault() ??
// if no groups at all, return empty sequence:
Enumerable.Empty<CustomerSettlementViewModel>();
Note: the FirstOrDefault is the first step where the query is actually performed. If desired you could put everything in one big query. Not sure if this would improve readability and maintainability.

This is my syntet error, I need to write this since the first
var settlements = from c in _customerRepository.GetAllQueryable()
join cs in _customerSettlementRepository.GetAllQueryable() on new {c.CustomerMainC, c.CustomerSubC}
equals new {cs.CustomerMainC, cs.CustomerSubC}
select new CustomerSettlementViewModel()
{
TaxRate = cs.TaxRate
};
settlements = settlements.Where(p => p.ApplyD == settlements.Max(b => b.ApplyD));

Related

Converting a field to lower case and merging data in an sqlite database

I need to merge some randomly uppercased data that has been collected in an SQLite table key_val, such that key is always lowercase and no vals are lost. There is a unique compound index on key,val.
The initial data looks like this:
key|val
abc|1
abc|5
aBc|1
aBc|5
aBc|3
aBc|2
AbC|1
abC|3
The result after the merge would be
key|val
abc|1
abc|2
abc|3
abc|5
In my programmer brain, I would
for each `key` with upper case letters;
if a lower cased `key` is found with the same value
then delete `key`
else update `key` to lower case
Re implementing the loop has a sub query for each row found with upper case letters, to check if the val already exists as a lower case key
If it does, I can delete the cased key.
From there I can UPDATE key = lower(key) as the "duplicates" have been removed.
The first cut of the programming method of finding the dupes is:
SELECT * FROM key_val as parent
WHERE parent.key != lower(parent.key)
AND 0 < (
SELECT count(s.val) FROM key_val as s
WHERE s.key = lower(parent.key) AND s.val = parent.val
)
ORDER BY parent.key DESC;
I'm assuming there's a better way to do this in SQLite? The ON CONFLICT functionality seems to me like it should be able to handle the dupe deletion on UPDATE but I'm not seeing it.
First delete all the duplicates:
DELETE FROM key_val AS k1
WHERE EXISTS (
SELECT 1
FROM key_val AS k2
WHERE LOWER(k2.key) = LOWER(k1.key) AND k2.val = k1.val AND k2.rowid < k1.rowid
);
by keeping only 1 combination of key and val with the min rowid.
It is not important if you kept the key with all lower chars or not, because the 2nd step is to update the table:
UPDATE key_val
SET key = LOWER(key);
See the demo.
Honestly it might just be easier to create a new table and then insert into it. As it seems you really just want a distinct select here, use:
INSERT INTO kev_val_new ("key", val)
SELECT DISTINCT LOWER("key"), val
FROM key_val;
Once you have populated the new table, you may drop the old one, and then rename the new one to the previous name:
DROP TABLE key_val;
ALTER TABLE key_val_new RENAME TO key_val;
I agree with #Tim that it would be easire to re-create table using simple select distict lower().. statement, but that's not always easy if table has dependant objects (indexes, triggers, views). In this case this can be done as sequence of two steps:
insert lowered keys which are not still there:
insert into t
select distinct lower(tr.key) as key, tr.val
from t as tr
left join t as ts on ts.key = lower(tr.key) and ts.val = tr.val
where ts.key is null;
now when we have all lowered keys - remove other keys:
delete from t where key <> lower(key);
See fiddle: http://sqlfiddle.com/#!5/84db50/11
However this method assumes that key is always populated (otherwise it would be a strange key)
If vals can be null then "ts.val = tr.val" should be replaced with more complex stuff like ifnull(ts.val, -1) = ifnull(tr.val, -1) where -1 is some unused value (can be different). If we can't assume any unused value like -1 then it should be more complex check for null / not null cases.

How to query AWS dynamodb to get for every one hour, for every day and for 30 days?

I wrote query like below. I am able to retrieve data fromtime and totime. My problem is for every minute they are 30 records. I would like to get help to get the first record for every one hour and 24 records for one day and I need this for 30 days.
var config = new QueryRequest
{
TableName = "dfgfdgdfg",
KeyConditionExpression = "id= :id AND plctime BETWEEN :fromtime AND :totime",
ExpressionAttributeValues = new Dictionary<string, AttributeValue> {
{
":serialNumber", new AttributeValue {S = id}
},
{
":fromtime", new AttributeValue {S = fromtime }
},
{
":totime", new AttributeValue {S = totime }
}
},
};
return await _dynamoClient.QueryAsync(config);
In addition to storing your record as is, you could consider inserting another record that looks like this :
{
pk : "DailyMarker_" + DateTime.Now.ToString("yyMMdd"), // partition key
sk : "HourlyMarker_" + DateTime.Now.ToString("yyMMddhh") // range key
record: <your entire record>
}
pk and sk would be of the structure DailyMarker_201911 and HourlyMarker_2019112101. Basically the part after the underscore acts as a date/time stamp with only the granularity you are interested in.
While inserting a marker record, you can add precondition checks, which, if they fail, will prevent the insertion from taking place (see PutItem -> ConditionExpression. This operation throws an exception with most SDKs if the condition evaluates to false, so you want to handle that exception.
At this point only the first record per hour is being inserted into this PK/SK combination, and all SKs for one day end up under the same PK
To query for different ranges, you will have to perform some calculations in your application code to determine the start and end buckets (pk and sk) that you want to query. While you will need to make one call per pk you are interested in, the range key can be queried using range queries
You could also switch the pk to be monthly instead of daily, so that will reduce the number of PKs to query while increasing the potential for imbalanced keys (aka. hot keys)

ASP.NET returns incorrect data from oData Controller

I have an ASP.NET Web API app using Oracle's Entity Framework driver. I have an entity defined for a view as follows:
CREATE OR REPLACE FORCE VIEW "PHASE_TWO"."EDIPRODUCT" ("ID", "STK_NUM", "TITLE", "ISBN", "UPC", "ITEMNO", "LONGFORMAT", "ABRIDGED", "WEB_TITLES_ID", "OCLC", "GENRE", "RELYEAR", "ORIG_REL", "LANG", "ORIG_STKNUM", "PUBLISHER", "PEOPLELIST", "SALES_ORG", "NOT_AVAIL") AS
SELECT sap_product.id,
sap_product.stk_num,
sap_product.longdesc AS title,
sap_product.isbn,
sap_product.upc,
sap_product.itemno,
sap_product.longformat,
sap_product.abridged,
mwt_product.web_titles_id,
mwt_product.oclc,
mwt_product.genre,
mwt_product.RELYEAR,
sap_product.orig_rel,
sap_product.lang,
sap_product.orig_stknum,
UPPER (publisher.name) publisher,
(SELECT LISTAGG (p.FULLNAME, ', ') WITHIN GROUP (
ORDER BY pp.rank) AS People
FROM people p
JOIN product_people pp
ON p.id = pp.peopleid
WHERE pp.stk_num = sap_product.stk_num
GROUP BY pp.STK_NUM
) PeopleList,
sppg.PRICING_TYPE as sales_org,
sap_product.not_avail
FROM sap_product
JOIN mwt_product ON sap_product.stk_num = mwt_product.stk_num
JOIN publisher ON mwt_product.publisherid = publisher.id
JOIN SAP_PRODUCT_PRICING_GROUP sppg on sppg.STK_NUM = mwt_product.stk_num and sppg.MARKED_FOR_DELETION = 0
WHERE mwt_product.WEB_PRODUCTS_ID > 0;
This view works as expected in SQL Developer. My getEDIPRODUCT function (yes, it's VB.NET) in my controller is as follows:
' GET: odata/EDIPRODUCTs
<EnableQuery>
Function GetEDIPRODUCT() As IQueryable(Of EDIPRODUCT)
Dim results As IQueryable
results = db.EDIPRODUCT
For Each _product In results
Console.Write(_product)
Next
Return results
End Function
I just added the for loop in order to inspect the results. What I see when I inspect the results is the same product record is returned for each row. The value for the ID is duplicate and the only other field that should have variant values (sppg.PRICING_TYPE as sales_org) also just repeats.
I have other views where this does not occur. The correct number of records are always returned, but the first record retrieved is always just repeated in each row of the result set. Any idea what could be going on here?
I never actually resolved this issue and am still interested in why this fails, but I rewrote the portion of the app that uses this view to use OData's $expand to retrieve the related data.

nature of SELECT query in MVC and LINQ TO SQL

i am bit confused by the nature and working of query , I tried to access database which contains each name more than once having same EMPid so when i accessed it in my DROP DOWN LIST then same repetition was in there too so i tried to remove repetition by putting DISTINCT in query but that didn't work but later i modified it another way and that worked but WHY THAT WORKED, I DON'T UNDERSTAND ?
QUERY THAT DIDN'T WORK
var names = (from n in DataContext.EmployeeAtds select n).Distinct();
QUERY THAT WORKED of which i don't know how ?
var names = (from n in DataContext.EmployeeAtds select new {n.EmplID, n.EmplName}).Distinct();
why 2nd worked exactly like i wanted (picking each name 1 time)
i'm using mvc 3 and linq to sql and i am newbie.
Both queries are different. I am explaining you both query in SQL that will help you in understanding both queries.
Your first query is:
var names = (from n in DataContext.EmployeeAtds select n).Distinct();
SQL:-
SELECT DISTINCT [t0].[EmplID], [t0].[EmplName], [t0].[Dept]
FROM [EmployeeAtd] AS [t0]
Your second query is:
(from n in EmployeeAtds select new {n.EmplID, n.EmplName}).Distinct()
SQL:-
SELECT DISTINCT [t0].[EmplID], [t0].[EmplName] FROM [EmployeeAtd] AS
[t0]
Now you can see SQL query for both queries. First query is showing that you are implementing Distinct on all columns of table but in second query you are implementing distinct only on required columns so it is giving you desired result.
As per Scott Allen's Explanation
var names = (from n in DataContext.EmployeeAtds select n).Distinct();
The docs for Distinct are clear – the method uses the default equality comparer to test for equality, and the default comparer sees 4 distinct object references. One way to get around this would be to use the overloaded version of Distinct that accepts a custom IEqualityComparer.
var names = (from n in DataContext.EmployeeAtds select new {n.EmplID, n.EmplName}).Distinct();
Turns out the C# compiler overrides Equals and GetHashCode for anonymous types. The implementation of the two overridden methods uses all the public properties on the type to compute an object's hash code and test for equality. If two objects of the same anonymous type have all the same values for their properties – the objects are equal. This is a safe strategy since anonymously typed objects are essentially immutable (all the properties are read-only).
Try this:
var names = DataContext.EmployeeAtds.Select(x => x.EmplName).Distinct().ToList();
Update:
var names = DataContext.EmployeeAtds
.GroupBy(x => x.EmplID)
.Select(g => new { EmplID = g.Key, EmplName = g.FirstOrDefault().EmplName })
.ToList();

Comparing Two Queries and Synchronizing Results

I have a table (Rooms) which contains a number of rows. Each row represents a room and each room needs to exist twice (once for fall, once for spring semester). Sometimes when folks add a room they only add it for one semester. I'm working on a process that will synchronize the rooms between semesters.
First I've pulled two queries, one that gets all of the rooms with fall in their semester column and one that gets all of the rooms with spring in their semester column, like so:
Dim getFallRooms = (From p In dbContext.Rooms _
Where p.semester = "Fall" _
Select p)
Dim getSpringRooms = (From p In dbContext.Rooms _
Where p.semester = "Spring" _
Select p)
The results will each contain multiple rows with the following columns: id, building, room, occupant, and semester.
What I want to do is something like this (pseudo):
For Each row in getFallRooms
If row.building and row.room not in getSpringRooms Then
' Code to add new row with fall as the semester.
End If
Next
Any suggestions on how I can make this into actual, workable code?
For Each row in getFallRooms
If (From p in getSpringRooms
Where p.building = row.building
AndAlso p.room = row.room).Count = 0 Then
' Code to add new row with fall as the semester.
End If
Next
You could do something quite similar to what's being done at this link.
I don't know precisely how to do it in VB.NET, but here's how I would do it in C#, using the LINQ extension methods:
var fallRooms = dbContext.Rooms.Where(room => room.semester.Equals("Fall")).Select(r => r.Name);
var nonSpringRooms = dbContext.Rooms.Where(room => room.semester.Equals("Spring"))
.Select(r => r.Name)
.AsQueryable()
.Except(fallRooms);
Afterwards, you could then do a For Each loop on nonSpringRooms to do whatever it is that you'd want to do.
On a side note, someone should correct me if I'm wrong, but I believe that the above code would only go to the database once. Hence, you'd get that benefit as well.
EDIT: I realized that since they'd be on the same table that the records would have the same primary key. I've subsequently changed the query above, assuming that you have a name field that would be the same in both records.

Resources