How to determine which SQL entries are missing after querying? - c#

Given the following database structure and using the EntityFramework.
Every five minutes, the 'phasecount' table gets records for each record in 'Phase'.
using (Entities db = new Entities())
{
db.ContextOptions.LazyLoadingEnabled = false;
int numberofcontrollers = (from a in db.Junctions select a).Count();
List<int> controllerids = (from b in db.Junctions select b.Id).ToList();
var configuration = (from c in db.Configurations select c).First();
DateTime laststamp = (from s in db.Stamps select s.Time).Max();
DateTime firststamp = laststamp.AddMinutes(-1 * (CountIntervalsBefore - 1) * TimeSliceLength);
var stamps = from s in db.Stamps.Include("PhaseCounts.Phase") where s.Time >= firststamp && s.Time <= laststamp orderby s.Id select s;
// check consistency; number of stamps should equal timeslices*controllers
if (stamps.Count() != CountIntervalsBefore * numberofcontrollers)
{
//counts are not available for all timeslices and controllers
//do extended consistency check (and use dummy data?)
}
}
I want to select for one hour each phasecount for all phases.
stamps normally equals 72, ie 12 5-minute slices * 6 junctions.
If it doesn't equal 72, how to determine which phases and which timestamps have missing data?

My first thoughts on a solution; this may not be the optimal search method but it should work.
Take the earliest timestamp in the group, and check that you do indeed have the correct number of records with that timestamp (from what you've said, I believe this is 6). From that you can tell if there are any missing from this set. Then look for the closest timestamp to the current one. If it's significantly more than about five minutes, you have a whole set missing. If it's around five minutes, check that you have the correct number of records with that timestamp, like with the first set. Repeat until either (total missing records + total found records = 72) or you run out of records. If you get to the end of the records and you still have some missing, then the earliest timestamp was not the first one, and you have a complete set missing there as well. At this point, either (total missing records + total found records = 72) or something has gone very wrong.

Related

How can I refactor this LINQ to be more performant?

I have a console app that processes orders generated since 18:30 on the previous day. The method returns an array of order numbers.
The first operation is to retrieve a list of orders from a table called Docstore (which contains orders that have been printed, which are the only orders that are needed) into an array:
string[] orders;
using (var db = new TCRContext())
{
var query = from docs in db.Docstore
where docs.DateCreated >= fromDate && docs.DocumentType == "InvoiceCredit"
select docs.PrimaryKey.Trim();
orders = query.Distinct().ToArray();
}
Typically there may be at most 40 such orders. There are a couple of further considerations. First, there's a database VIEW called MatchingCustomerOrders which JOINs Orders (from the actual Order table, not the Docstore table) onto another table CPCustomers which contains a subset of all the company's Customers who participate in this particular operation (about 90 out of 2,370).
CREATE OR ALTER VIEW [dbo].[MatchingCustomerOrders]
AS
SELECT
Orders.order_no,
Orders.invoice_date
FROM
Orders INNER JOIN
dbo.CPCustomer ON Orders.customer = dbo.CPCustomer.Customer
WHERE
(dbo.CPCustomer.IsDeleted = 0)
GO
So we only need orders that match these customers. And we want to restrict them to those which match the invoice date parameters (i.e. from 18:30 the previous day to now) and also we don't want to process orders that have already been processed (hence the ProcessedInvoices part)
var query = from matches in db.MatchingCustomerOrders
where orders.Contains(matches.order_no.Trim()) &&
!(from processed in db.ProcessedInvoices select processed.order_no.Trim()).Contains(matches.order_no.Trim()) &&
matches.invoice_date >= fromDate.Date && matches.invoice_date <= toDate
select matches.order_no;
orders = query.Distinct().ToArray();
This is not particularly performant because the VIEW MatchingCustomerOrders has 90,000+ rows, and my suspicion is that LINQ is operating over the whole set. I've been banging my head trying to refactor this but none of my solutions seems to work.

Calculate a Running Total plus minus with merge column

I have a table with five column description, opening balance, sale, sale return, recipt.
I want to merge opening balance, sale as "Debit" and salereturn, recipt as "Credit".
How to calculate running total as column name as "balance" debit amount plus and credit amount MINUS in balance column?
My attempt is
SELECT Description, (InvoiceAmount + OpeningBalance) as 'Dabit', (DrAmount + SaleReturn + BadDebtAmount) as 'credit', SUM (sale+ OpeningBalance-SaleReturn-recipt) over (ORDER BY id) AS RunningAgeTotal FROM tablename
You seem to be describing coalesce() and a window function:
select description,
coalesce(opening, sale) as debit,
coalesce(return, receipt) as credit,
sum(coalesce(opening, sale, 0) - coalesce(return, receipt, 0)) over (order by order by (case description when 'opening balance' then 1 when 'sale' then 2 when 'sale return' then 3 else 4 end))
from t
order by (case description when 'opening balance' then 1 when 'sale' then 2 when 'sale return' then 3 else 4 end);
At the expense of creating a temporary list, a Linq version would be as follows :
Assuming your original source is from a Sql database, then you first need to bring the data into memory, eg
var records = OrderDetails
.OrderBy(a=>a.Date)
.Select(a => new
{
a.Description,
Debit = a.OpeningBalance + a.Sale,
Credit = a.Return + a.SaleReturn
}
)
.ToList();
Note the query needs to be sorted to ensure the date is returned in the correct order. You haven't mentioned any other fields, so I have just assumed there is a field called date that can be used.
Once you have the data in memory, you can then add the Balance column, ie
decimal balance = 0;
var list = records.Select(a => new
{
a.Description,
a.Debit,
a.Credit,
Balance = balance += (a.Debit - a.Credit),
}).ToList();
Because you are introducing a local variable and initialising it outside the Linq statement, it is important that the query is not enumerated twice unless balance has been reset to zero. You can avoid this by using .ToList(); or .ToArray();

Proper use of DbFunctions.CreateDateTime()

I've got a database tabel 'DateExcluded' containing 3 int columns : Year, Month and Day, the latter being the daynumber of the month. I want their combination evaluated in an entity query, to retrieve all rows before one year from current date like so :
var l =
(from p in c.DateExcluded
where
DbFunctions.CreateDateTime(p.Year, p.Month, p.Day, null, null, null)
<= DateTime.Now.AddYears(1)
select p).ToList();
This query always returns 0 columns which it shouldn't. Wrong use of DbFunctions.CreateDateTime?
I've got a database tabel 'DateExcluded' containing 3 int columns : Year, Month and Day.
Don't ever create a column for each year, month and day
You're creating a non-sargable query, also know as the worst performing query you can create.
The correct way is to actually use a DateTime field. Then your query is just correct without any incorrect math possible.
var l =
(from p in c.DateExcluded
where
c.DateExcluded < DateTime.Now.AddYears(1).AddDay(1)
select p)
.ToList();
If you still want to use DbFunctions.CreateDateTime, avoid null values as it won't work correctly (see why in comment by Ole EH Dufour).
You should pass 0 instead of null, like this:
DbFunctions.CreateDateTime(p.Year, p.Month, p.Day, 0, 0, 0)

one query with groupby / count / select new

I have to make in c# a query with linq to sql. I can handle it in sql but in linq to
sql is the result not what I wanted to get.
So there is a table with:
a day, in datetime with date and time
and a kind of id
I have to count the ids for each date, the time isn't important. So the result
should be something like:
day: 2013-11-12 amountIDs: 4
People said to me, I can make a select new query and in this query I can set the day
and could count the ids, or I make a group by day. I read similar question, but it doesn't work in my case.
Could somebody help me?
I tried it with the statement below, but the days have to be grouped, so now the output is foreach datetime, like this
day: 12.12.2013 12:00:00 amountIDs: 1
day: 12.12.2013 12:10:10 amountIDs: 1
In sql I made this statement:
SELECT CONVERT(VARCHAR(20), data.dayandtime, 106) AS day, count(data.amountIds) as ids
FROM data
WHERE ( data.dayandtime >= DATEADD(day, -28, getdate()) AND (data.type = 100) AND (data.isSomething = 0) )
GROUP BY CONVERT(VARCHAR(20), data.dayandtime, 106), data.isSomthing and it works.
I saw similar cases, where people made a : from-select-new xyz statement, than I made a view of it and tried to group just the view. Like this
var query = data.GroupBy(g => g.day.Value).ToList();
var qry = from data in dbContext
group data by data.day into dataGrpd
select new
{
day= dataGrpd.Key,
amountIDs= dataGrpd.Select(x => x.Id).Distinct().Count()
};
Check This

C# Linq Weighted Average Based on Date

I've found several posts detailing how to perform a weighted average based on a foreign key, but I have yet to find a solution that deals with my situation. Here it is:
I have two tables, table A and a table B many-to-many table linking them; nothing complicated:
TableA
{
A_ID,
Other stuff
}
TableB
{
B_ID,
Date
Other stuff
}
LinkAtoB
{
A_ID,
B_ID
}
Now here comes the math part. I'm more or less trying to weight result from TableA based on the number of recent associations in Table B.
So if TableA has 4 associations in table with the following dates:
{10/23/2010, //3 days ago
10/19/2010, //5 days ago
10/18/2010, //6 days ago
9/13/2010} //40ish days ago
So here is how I'd like to rank them:
I'd like to provide a recency threshold in days, I'll use 7 days as an example:
So using the above data I would assign the following values:
{10/23/2010, //7-3 = 4
10/19/2010, //7-5 = 2
10/18/2010, //7-6 = 1
9/13/2010} //40ish days ago
So the value of the weighted average for that particular TableA entry is then 7 / 3 = 2.33333.
Here is more or less what I have so far:
var k = from a in TableA
group a by a.Select(x=>x.LinkAtoB.TableB)
.Where(x=>x.Date.CompareTo(DateTime.Now.AddDays(-7)) >= 0)
into g
select g.Sum(x => DateTime.Now.Subtract(x.Date).Days) /
g.Sum(x => x.Length);
I think I'm close but I know I have the group part wrong. I think the other stuff should work. How do I fix my code to accomplish what I want?
Here you go! :)
var k = (from b in TableB
join bb in LinkAtoB on b.B_ID equals bb.B_ID into b_join
from ab in b_join.DefaultIfEmpty()
where b.B_DATE.CompareTo(DateTime.Now.AddDays(-7)) > 0
select new {ab.A_ID, DaysAgo = (DateTime.Now - b.B_DATE).Days} into xx
group xx by xx.A_ID into yy
select new {yy.Key, Weighted = yy.Sum(x=> 7 - x.DaysAgo) / yy.Count()} into zz
join a in TableA on zz.Key equals a.A_ID
select new {a.A_ID, a.A_Other_Stuff, zz.Weighted}).ToList();

Categories

Resources