I'm stuck on one part whereby I have no idea how to solve it. Basically, I have one table, "Shifthours" and another one which is "employeeshift". Shifthours table have shift_Start and shift_Stop. employeeshift table have StartTime and EndTime. I'm comparing shift_Start and StartTime. I have linked this 2 tables together using foreign key and the question I asked is that I want the shift_Start to compare with the StartTime and shift_Stop to compare with the EndTime and see the employee fit which shift and the shift_Start and shift_Stop will appear at the column that the employee is eligible.
Currently I got a code that only joins 2 table together but not comparing the timings.
private void LoadAllEmpShift()
{
using (testEntities Setupctx = new testEntities())
{
BindingSource BS = new BindingSource();
var Viewemp = from ES in Setupctx.employeeshifts
join shifthour sh in Setupctx.shifthours on ES.ShiftHourID equals sh.idShiftHours
select new
{
ES.EmployeeShiftID,
ShiftHour_Start = sh.shiftTiming_start,
ShiftHour_Stop = sh.shiftTiming_stop,
ES.EmployeeName,
ES.StartTime,
ES.EndTime,
ES.Date
};
BS.DataSource = Viewemp;
dgvShift.DataSource = BS;
}
}
Anyone knows how to do this?
Edit:
You said you were trying to find where the employee hours match with a set of shift times. It would be nice to have some sample data and the algorithm that you want to use to determine what is a good shift time match.
I have assumed here that the best way to do that is to base the employee's start time off the nearest shift start time.
In the following code, I use the let function to essentially look through the shift hours and find the set of shift hours that are nearest to the employee's start time.
var Viewemp = from ES in Setupctx.employeeshifts
join sh in Setupctx.shifthours on ES.ShiftHourID equals sh.idShiftHours
into shifts
let diff = shifts
.OrderBy (s =>
// this is the line that needs attention:
System.Math.Abs((int)(ES.StartTime - s.shiftTiming_start))
)
.First ()
select new
{
ES.EmployeeShiftID,
ShiftHour_Start = diff.shiftTiming_start,
ShiftHour_Stop = diff.shiftTiming_stop,
ES.EmployeeName,
ES.StartTime,
ES.EndTime,
ES.Date
};
Update
My type in database for the StartTime and EndTime is string instead of
time
In the above code the important logic is finding the absolute value difference between ES.StartTime and s.shiftTiming_start and the smallest difference indicates the best match for shift hour. Unfortunately, your database stores this data as a string and you need to compare them as numeric.
Linq-to-Entities does not contain an easy way to convert string to int function.
I think your next step would be to look into how you can convert those string values to int values. Take a look at this question as I think it might help you out:
Convert String to Int in EF 4.0
Related
This question already has answers here:
"order by newid()" - how does it work?
(5 answers)
Closed 2 years ago.
I am creating a quiz and im using a random number from a range of 1 - 20 numbers ( Primary Keys)
Random r = new Random();
int rInt = r.Next(1, 9);
The numbers(primary keys) and then used for a query to select 5 random number but the problem is that I am getting repeated questions because the numbers repeat
string SQL = "SELECT QuestionText,CorrectAnswer,WrongAnswer1,WrongAnswer2,WrongAnswer3 FROM Question Where QuestionID = " + rInt;
I have tried some methods to fix it but its not working and running out of ideas , anyone have any suggestions?
Just ask the database for it:
string SQL = #"
SELECT TOP 5 QuestionText,CorrectAnswer,WrongAnswer1,WrongAnswer2,WrongAnswer3
FROM Question
ORDER BY NewID()";
If/when you outgrow this, there exists a more optimized solution as well:
string SQL = #"
WITH cte AS
(
SELECT TOP 5 QuestionId FROM Questions ORDER BY NEWID()
)
SELECT QuestionText,CorrectAnswer,WrongAnswer1,WrongAnswer2,WrongAnswer3
FROM cte c
JOIN Questions q
ON q.QuestionId = c.QuestionId
";
The second query will perform much better (assuming QuestionId is your primary key) because it will only have to read the primary index (which will likely already be in memory), generate the Guids, pick the top 5 using the most efficient method, then look up those 5 records using the primary key.
The first query should work just fine for smaller number of questions, but I believe it may cause a table scan, and some pressure on tempdb, so if your questions are varchar(max) and get very long, or you have tens of thousands of questions with a very small tempdb with some versions of Sql Server, it may not perform great.
Something like this might do the trick for you:
[ThreadStatic]
private static Random __random = null;
public int[] Get5RandomQuestions()
{
__random = __random ?? new Random(Guid.NewGuid().GetHashCode()); // approx one in 850 chance of seed collision
using (var context = new MyDBContext())
{
var questions = context.Questions.Select(x => x.Question_ID).ToArray();
return questions.OrderBy(_ => __random.Next()).Take(5).ToArray();
}
}
Another, server side approach:
private static Random _r = new Random();
...
var seed = _r.NextDouble();
using var context = new SomeContext();
var questions = context.Questions
.OrderBy(p => SqlFunctions.Checksum(p.Id * seed))
.Take(5);
Note : Checksum is not bullet proof, limitations apply. This approach should not be used to generate quiz questions in life or death situations.
As per request:
SqlFunctions.Checksum will essentially generate a hash and order by it
CHECKSUM([Id] * <seed>) AS [C1],
...
ORDER BY [C1] ASC
CHECKSUM (Transact-SQL)
The CHECKSUM function returns the checksum value computed over a table
row, or over an expression list. Use CHECKSUM to build hash indexes.
...
CHECKSUM computes a hash value, called the checksum, over its argument
list. Use this hash value to build hash indexes. A hash index will
result if the CHECKSUM function has column arguments, and an index is
built over the computed CHECKSUM value. This can be used for equality
searches over the columns.
Note, as mentioned before the Checksum is not bullet proof it returns an int (take it for what it is), however, the chances of a collision or duplicate is extremely small for smaller data sets when using it in this way with unique Id, it's also fairly performant.
So running this only a production database with 10 million records many times, there was no collisions.
In regards to speed, it can get the top 5 in 75ms, however it is slower when generated by EF
The cte solution tendered for NewId, is about 125 ms.
The Linq .Distinct() method is too nice to not use here
The easiest way I know of doing this would be like below, using a method to create an infinite stream of random numbers, which can then be nicely wrangled with Linq:
using System.Linq;
IEnumerable<int> GenRandomNumbers()
{
var random = new Random();
while (true)
{
yield return rand.Next(1, 20);
}
}
var numbers = GenRandomNumbers()
.Distinct()
.Take(5)
.ToArray();
Though it looks like the generator method will run for ever because of its closed loop, it will only run until it has generated 5 distinct numbers, because of how it yields.
Try selecting all the questions from the db. Say you have them in a collection 'Question', you could then try
Questions.OrderBy(y => Guid.NewGuid()).ToList()
I have a table in my database that has columns 'price', 'percentage' and 'ID'.
Percentage column needs to be calculated using colum 'price' and 'id'. There is an initial price, followed by three increased prices, all of them having the same ID. I need to find a method that calculates the percentage column in my database using C#.
I attach a picture for better understanding how it should be.
I am writing an import system using Linq and I have already imported all the columns, but now I need to calculate percentage for the increasing prices and I am really struggeling with this. Maybe someone have some god suggestions of how I can solve this.
UPDATE:
public static void calculateProcentage(string id, double price, double, double percentage)
{
var percentageQuery = from l in db.Table
where l.ID == id
&& l.Price == price && l.Percentage != percentage
select l;
foreach (Table l in percentageQuery)
{
//double newPercentage = (double) l.Percentage;
//DataTable table = new DataTable();
// int rowCount = table.Rows.Count;
DataGridView dataGridView = new DataGridView();
for (int i = 0; i < dataGridView.Rows.Count; i++)
{
if (l.Building_vigor_ID == building_vigor_id)
{
//var priceRows = db.V_Leases.Select(x => x.Price_sqm).ToList();
// get a list of the rows of the price column
// get a list of the rows of the price column
var priceRows = (from r in db.Table
select r.Price).ToList();
percentage = (double)(priceRows[i] / priceRows[i + 1]);
}
}
}
try
{
db.SubmitChanges();
Console.Write("Percentage updated");
//Console.ReadKey();
}
catch (Exception e)
{
Console.WriteLine(e);
Console.Write("Could not update percentage");
//Console.ReadKey();
}
}
That is what I have tried. I bassicaly wanted to make it like an update method with only updating column percentage. But did not actualy work. I am pretty new with Linq sow it may be some bad code written here.
The disclaimer
I am pretty new with Linq sow it may be some bad code written here.
Your question, and the way you've attempted to solve this, are fraught with inconsistencies and seemingly contradictory expectations.
As it stands, the question you've asked is not answerable due to lack of information. However, I do think I understand what the actual problem is that you're trying to solve, so I'll give you that answer. But first, let me explain how I've interpreted your question.
My assumptions about your actual question
As I understand it, you're trying to calculate the percentage based on the previous value.
A more generalized example:
PRICE % ID
------------------------
100 A 1
103 B 1
100 C 2
150 D 2
A and C should both be 0 as they are the "base" price of their respective ID value.
B should be 3% because its price is 103% of A's price.
D should be 50% because its price is 150% of C's price.
My below answer will assume that this is correct.
There is also a problem with your expected values. In your example, you have listed the percentage of 19.79 (compared to 19.21) as 0.3.
This does not make sense. The difference is 3%. There are two different (acceptable) ways to denote this in the percentage column:
3, because it is expressed as a percentage (3%)
0.03, because it is expressed as a decimal value (3% = 0.03)
Your usage of 0.3 makes no sense, as I would interpret this as either 0.3% (option 1) or 30% (option 2).
In order to maintain consistency, my answer will assume that you want the decimal value (0.03 in the above example)
I assume your data class is constructed as follows:
public class Foo
{
public decimal Price { get; set; }
public decimal Percentage { get; set; }
public int ID { get; set; }
}
I don't quite understand how you're using the method. You supply three parameters (string id, double price, double, double percentage), but then you go on to select your data as follows:
var percentageQuery = from l in db.Table
where l.ID == id
&& l.Price == price && l.Percentage != percentage
select l;
It makes little sense to supply a percentage, and then pick everything that's different from that percentage. You have no idea of knowing what data you're going to get, in what order, and whether or not the found entries are "before" or "after" your mentioned percentage.
Instead, my answer will be a method that recalculates all percentages of a given ID. This seems like a much clearer algorithm.
The assumed answer
Retrieving the data
Your attempt is a weird mix of new and old technologies (LINQ, DataTable), I'm going to use LINQ near exclusively, as I feel the use of DataTable is unwarranted here.
public static void CalculatePercentagesForID(int id)
{
Foo[] rows = db.Table.Where(x => x.ID == id).ToArray();
//... (see the next code block)
}
This is much simpler. Note that I am assuming that you wish to process the entries based on the order that they appear in the database. If you need to order them based on something else (e.g. a date value in your Foo objects), then you will have to use an additional OrderBy, e.g. :
Foo[] rows = db.Table.Where(x => x.ID == id).Orderby(x => x.DateCreated).ToArray();
Processing the data
It's important to notice here that a percentage is calculated off of two (subsequent) rows.
//The first entry is always the "base" price, therefore always 0.
rows[0].Percentage = 0;
for(int i = 1; i < rows.Length; i++)
{
var previous = rows[i-1];
var current = rows[i];
current.Percentage = CalculateDifferenceInPercentage(previous.Price, current.Price);
}
//TODO: save all the objects in "rows" back to the database!
Notice how the for loop starts at 1, not at 0. We skip step 0 because the first element is automatically 0 anyway. The for loop will only process every row after the first.
Calculating the percentage
public static Decimal CalculateDifferenceInPercentage(decimal oldPrice, decimal newPrice)
{
//1.The elaborate calculation
//The comments are example output when oldPrice = 19.21, newPrice = 19.79
var theRatio = newPrice / oldPrice;
// = 1.0301...
var theRatioRounded = Math.Round(theRatio,2);
// = 1.03
var thePercentageDifference = theRatioRounded - 1;
// = 0.03
return thePercentageDifference;
}
Or, if you want a shorter (but harder to read) version:
public static Decimal CalculateDifferenceInPercentage(decimal oldPrice, decimal newPrice)
{
return Math.Round(newPrice / oldPrice , 2) - 1;
}
Some caveats
I've omitted some null-checks for the sake of simplicity, e.g. checking if any rows are returned.
I've omitted how to save the updated entities in the database as it is not pertinent to your actual question about calculating the percentages.
This works both for negative and positive percentages.
Trying to update a table in ASP.net MVC with a Linq query. It works fine in terms of actually doing the update, but I can't figure out how to mathematically update it. For example, the Printer Credit Fund was set to "50", added 12 to it then sets it "5012" as opposed to "62". The following is the code that is doing the update:
tblOption fund = (
from n in db.tblOptions
where n.ID == 1
select n).First();
fund.PrinterCreditFund = fund.PrinterCreditFund + tblPrinterCredit.Money;
We're using Entity Framework if that makes any sort of a difference.
Any tips towards the right direction of doing a mathematical update rather than appending the value would be hugely appreciated.
Many thanks
That's it. Convert fund.PrinterCreditFund and tblPrinterCredit.Money to using Convert.ToInt32() and also make sure fund.PrinterCreditFund is also int type
tblOption fund = (
from n in db.tblOptions
where n.ID == 1
select n).First();
fund.PrinterCreditFund = Convert.ToInt32( fund.PrinterCreditFund) + Convert.ToInt32( tblPrinterCredit.Money);
I guess that either Money is a string. Then the + operator concats them instead of summing the values. So both must be a numeric type, for example int:
fund.PrinterCreditFund = fund.PrinterCreditFund + int.Parse(tblPrinterCredit.Money);
Of course it would be better to not store a number as string in the first place.
I have a column calendar week and a column amount. Now I want to sum up the amount for every 4 calendar weeks starting from the first calendar week in April. E.g. if I have 52 (rows) calendar weeks in my initial table I would have 13 (rows) weeks in my final table (I talk about table here since I will try to bind the outcome later to a DGV).
I am using Linq-to-dataset and tried different sources to get a hint how to solve this but group by, aggregate couldnt help me but maybe there are some applications of them that I dont understand so I hope one of you Linq-Experts can help me.
Usually I post code but I can just give you the backbone since I have no starting point.
_dataset = New Dataset1
_adapter.Fill(_dataset.Table1)
dim query = from dt in _dataset.Table1.AsEnumerable()
Divide the week by four (using integer division to truncate the result) to generate a value that you can group on.
You can group by anything you like. You could, theoretically, use a simple incrementing number for that:
var groups = dt
.Select((row, i) => Tuple.Create(row, i / 4))
.GroupBy(t => t.Item2);
(c# notation)
Then you can calculate the sum for each group:
var sums = groups
.Select(g => g.Sum(t => t.Item1.Amount));
You mention you want to start at a certain month, e.g. April. You can skip rows by using Skip or Where:
dt.Skip(12).Select(...)
i will always start at 0, making sure your first group contains 4 weeks. However, to know exactly how many weeks to skip or where to start you need more calendar information. I presume you have some fields in your rows that mention the corresponding week's start date:
dt.Where(row => row.StartDate >= firstOfApril).Select(...)
first convert the data into a lookup with the month and then the amount. Note your columns need to be called CalendarWeek and Amount. Then convert to a dictionary and sum the values per key:
var grouped = dt.AsEnumerable().ToLookup(o => Math.Ceiling((double) o.Field<int>("CalendarWeek")/4), o => o.Field<double>("Amount"));
var results = grouped.ToDictionary(result => result.Key, result => result.Sum());
I would like some help making this comparison faster (sample below). The sample take each value in an array, attach an hour to a comparison-variable. If no matching value, it's add the value to a second array (which are concatenated later).
if (ticks.TypeOf == Period.Hour)
while (compareAt <= endAt)
{
if (range.Where(d => d.time.AddMinutes(-d.time.Minute) == compareAt).Count() < 1)
gaps.Add(new SomeValue() {
...some dummy values.. });
compareAt = compareAt.AddTicks(ticks.Ticks);
}
This execution is too consuming when came to i.e. hours. There are 365 * 24 = 8760 values at most in this array. In future, there will also be minutes/seconds per month 60*24*31=44640, which means unusable.
If the array most often was complete (which means no gaps/empty slots), it could easily be by-passed with if (range.Count() == (hours/day * days)). Though, that day will not be today.
How would I solve it more effective?
One example: If ther are 7800 values in the array, we miss about 950, right? But can I find just the gaps-endings, and just create the missing values? That would make the o-notation depend on amount of gaps, not the amount of values..
One other welcome answer is just an more effective loop.
[Edit]
Sorry for bad english, I try my best to describe.
Your performance is low because the range lookup is not using any indexing and rechecks the entire range every time.
One way to do this a lot quicker;
if (ticks.TypeOf == Period.Hour)
{
// fill a hashset with the range's unique hourly values
var rangehs = new HashSet<DateTime>();
foreach (var r in range)
{
rangehs.Add(r.time.AddMinutes(-r.time.Minute));
}
// walk all the hours
while (compareAt <= endAt)
{
// quickly check if it's a gap
if (!rangehs.Contains(compareAt))
gaps.Add(new SomeValue() { ...some dummy values..});
compareAt = compareAt.AddTicks(ticks.Ticks);
}
}