I've got 2 tables
Table 1 has a detail of progress of assignment in table 2
Table 1. Assignment
ID | Class | Content | StudentID | LastReviewDate
..............
3133 GC ABCD 1059 4/6/2018 17:35
Table 2. Detail
MondayOfWeekDate here is begin day of the week
AssignmentID | MondayOfWeekDate | Percent | StudentID | LastReviewDate
3133 3/19/2018 0 1059 3/23/2018 17:20
3133 4/2/2018 100 1689 4/7/2018 10:35
..............
I tried to write SQL to indicate between 3/26/2018 to 4/1/2018 to show assignment 3133 in that week but failed
SELECT Assignment.* FROM Assignment,Detail
WHERE Assignment.Class = 'GC'
AND Assignment.ID=Detail.AssignmentID
AND Detail.MondayOfWeekDate >'2018/03/26'
AND Percent<100
Of course the result was nothing but i can't find the right code to execute
Can you please me how to write SQL to show that information properly ? Thank you a lot.
SELECT Assignment.* FROM Assignment,Detail
WHERE Assignment.Class = 'GC'
AND Assignment.ID=Detail.AssignmentID
AND Detail.MondayOfWeekDate BETWEEN '2018-03-26' and '2018-04-01'
AND Percent<100
You will get no results as there is nothing between 3/26/2018 to 4/1/2018??
The only mistake you made is your date which is to use between and its a better idea to use iso formatted date yyyy-MM-dd
I cannot directly speak to why you don't see anything. But I can suggest how to write a more readable and maintainable query. This might fix your problems but I don't know.
Some simple guidelines:
Never use commas in the FROM clause.
Always use proper, explicit, standard JOIN syntax.
Use table aliases.
Use standard date formats.
So, start with a query like this:
SELECT a.*
FROM Assignment a JOIN
Detail d
ON a.ID = d.AssignmentID
WHERE a.Class = 'GC' AND
d.MondayOfWeekDate > '2018-03-26' AND
d.Percent < 100;
Also check for syntax errors when you run the query. It can be easy to make small errors that cause the query not to run.
Your query fails because the row
3133 4/2/2018 100 1689 4/7/2018 10:35
Is the one bigger than >'2018/03/26' but its Percent is not <100, but 100.
What are you exactly looking for?
Related
We have data as follows
Created On
Supplier
01-Nov-21
ABC
02-Nov-21
XYZ
15-Nov-21
ABC
20-Nov-21
MNO
25-Nov-21
ABC
So we want to record in descending order of creation but 1st want all "ABC" records on the top.
I tried with this one, but didn't worked.
object.OrderByDescending(m => m.name== "ABC").ThenBy(x => x.CreatedOn)
So instead of getting records as follows
01-Nov-21 | ABC
15-Nov-21 | ABC
25-Nov-21 | ABC
02-Nov-21 | XYZ
20-Nov-21 | MNO
It was coming in same order as above i.e. based on createdOn field
Am i missing anything here?
in descending order of creation but 1st want all "ABC" records on the top
You need to use ThenByDescending - using OrderByDescending initially doesn't then set the sorting of the date up to be descending too
I don't personally think you should sort a bool in your first clause, because it's not obvious to everyone how booleans sort. Rather than it be something one has to remember/look up, I would recommend that you use the bool to generate an int for sorting because everyone can easily understand your intent (everyone can put 0 and 1 into ascending order):
foreach(var x in list.OrderBy(m => m.name == "ABC" ? 0 : 1).ThenByDescending(x => x.CreatedOn))
//process your objects in turn here
To achieve a "date descending but all ABC on top" you first sort on whether something is ABC or not (and bear in mind that that's an exact, case sensitive match, unless this query is being translated to SQL and send to a DB like SQL server, where case sensitivity depends on the collation of the column being sorted/where trailing spaces may be ignored), then you want to sort the date descending with ThenByDescending.
Background
I have a table that contains only one column: Name.
There are only four rows in it, say
| Name |
| test1.com |
| test2.com |
| test3.com |
| test4.com |
Problem
If I query
var email = "a#test2.com";
Table.Where(x => email.EndsWith(x.Name));
I'll get an empty list. but If I query all rows first and calculate Where in memory like this
var email = "a#test2.com";
Table.ToList().Where(x => email.EndsWith(x.Name));
I'll get a list contains only test2.com which is correct.
The generated SQL for the first query is
SELECT "Extent1"."Name" AS "Name"
FROM "USER"."Table" "Extent1"
WHERE (( NVL(INSTR(REVERSE(:p__linq__0), REVERSE("Extent1"."Name")), 0) ) = 1)
I've tried replacing :p__linq__0 with 'a#test2.com' and running the query in the SQLDeveloper, the result is correct.
More Information
If I change EndsWith() to Contains(), the problem will be gone. Here is the generated SQL for Contains()
SELECT "Extent1"."Name" AS "Name"
FROM "USER"."Table" "Extent1"
WHERE (( NVL(INSTR(:p__linq__0, "Extent1"."Name"), 0) ) > 0)
Do you have any idea what's wrong with EndsWith or REVERSE method?
Environment
EF5.0
.NET4.5
Oracle11g
ODP.NET11.2 Release 3
This line concerns me and is a common pitfall with people using EF:
Table.ToList().Where(x => email.EndsWith(x.Name));
The part Table.ToList() is the worst part because this will actually materialise the entire table into memory and then perform the EndsWith in C#.
This line:
Table.Where(x => email.EndsWith(x.Name));
I would caution this approach just on general principle as it will be horrendously slow when the table grows to reasonable size. You can do the heavy lifting before the query hits the database by splitting out the domain from the the email as you construct the query:
var email = "a#test2.com";
/* You should null check this of course and not just assume a match was found */
var domain = Regex.Match(email , "#(.*)").Groups[1].Value;
/* Note: ToList() materialisation happens at the end */
var result = Table.Where(x => x.Name == domain).ToList();
Furthermore, if you need to match on the domain names of a column storing emails, then my preferred approach would be to split the email and store the domain name in a separate column that you index and just match on, this will scale and be a heck of a lot easier to manage. Remember that these days data is cheap... especially compared to non-indexable table scans.
Also remember (for both scenarios) that your database is set to CI (case insensitive)
I have a sql table called expenses with an int column called cost. In my application this data is displayed on a grid which is refreshing every time I insert a new row with a linq2sql insert. What I would like to do is have an integer variable in my application that is the sum of all the fields in the cost column every time I insert a row.
Is there a simple way to sum these fields with linq2sql every time I do an insert. Please try to avoid lambda as I haven't gotten to learning that yet.
Thanks!
Assuming that you use query syntax instead of lambdas, here you are:
var totalCost = (from expensesRow in dataContext.Expenses
select expensesRow.cost)
.Sum();
Which in fact is the same as:
var totalCost = dataContext.Expenses
.Sum(x => x.cost);
Here dataContext is an instance of your Linq2Sql DataContext class.
You get to learn lambdas today. http://www.theabsentmindedcoder.com/2010/06/linq-sum.html has exactly what you want, and uses a very simple lambda to get there. You could get away without the lambda by making your select gather up only the one column of integers you're trying to sum, but why do extra work to not learn things?
Say I had a table called users with the following structure:
[Table users] - id | username | points_awarded
Then, I can find the total number of points I awarded every user by running the query:
SELECT SUM(points_awarded) as total_points FROM users
You can also count how many users have points greater than our equal to N. For example, says N = 500, then I can run:
SELECT COUNT(id) as num_users_with_points FROM users WHERE points_award >= 500
Check out:
http://dev.mysql.com/doc/refman/5.1/en/counting-rows.html
http://dev.mysql.com/doc/refman/5.0/en/func-op-summary-ref.html
for more information.
I have a table which holds clocking in/out records for every user :
RecID User In/Out ClockInOutTime
8 1 IN 25/02/2011 09:36:44
9 1 OUT 25/02/2011 11:36:44
10 1 IN 25/02/2011 12:36:44
11 1 OUT 25/02/2011 17:36:44
12 1 IN 26/02/2011 00:00:00
13 1 OUT 26/02/2011 12:00:00
14 1 IN 26/02/2011 09:00:44
15 1 OUT 26/02/2011 12:36:44
Any ideas how I can work out the total time worked for every month using LINQ?
cheers
SELECT (SELECT SUM(DATEDIFF(SECOND,[ClockInOutTime], GETDATE()))
FROM [swp].[dbo].[Table_1] t1
WHERE [In/Out] = 'IN'
AND t1.[User] = t.[User]) -
Coalesce((SELECT SUM(DATEDIFF(SECOND,[ClockInOutTime], GETDATE()))
FROM [swp].[dbo].[Table_1] t2
WHERE [In/Out] = 'OUT'
AND t2.[User] = t.[User]),0)
FROM [swp].[dbo].[Table_1] t
GROUP BY [User]
SQL way to solve this, not the best, but works even when last event don't have OUT timestamp i.e when last session still hasn't been closed.
This is non-trivial to do in either Linq or SQL. There is no easy way to link each OUT record with the corresponding IN record in SQL.
You have two options:
Querying the data and calculating in code within a for loop.
Changing the table schema like: RecID, User, ClockInTime, ClockOutTime
Option 1 is easy to implement, but I would seriously consider option 2. How do you define in your business rules that each IN record must be followed by a corresponding OUT record (or be last record)?
TimeSpan output = new TimeSpan(0,0,0);
using (var enumerator = input.GetEnumerator())
{
while (enumerator.MoveNext())
{
var begin = enumerator.Current.ClockInOutTime;
if(!enumerator.MoveNext())
break;
var end = enumerator.Current.ClockInOutTime;
output += (end - begin);
}
}
Yes, it isn't LINQ but I wanted to offer a solution - secondly, if the dates aren't alternating (so after an IN is always an OUT) it'll break.
There is no solution that will only use linq. This is due to the fact that you need to introduce error handling as well (if a user forgets to sign out, usually there is a maximum time that will be applied then etc).
I would group the data by user, order it by date time and then run through it in a for each and do the calculation within the for each.
I have the following query:
;WITH valRules AS
( SELECT vr.valRuleID, Count(*) AS totalRows, Sum(vt.test) AS validRows
FROM (SELECT NULL AS x) AS x
JOIN #itemMap AS IM
ON IM.lngitemID = 1
JOIN tblValidationRule AS vr
ON IM.RuleID = vr.valRuleID
JOIN tblValidationRuleDetl AS vrd
ON vr.valRuleID = vrd.valRuleID
LEFT JOIN #ValTest AS vt
ON vrd.type = vt.type
AND vrd.typeSequence = vt.typeSequence
AND vrd.valRule & vt.Response > 0
OR (vrd.valrule = 0 AND vt.response = 0 )
GROUP BY vr.valRuleID
)
SELECT Count(*)
FROM valrules
WHERE totalrows = validRows
Note the CTE, and the Bitwise Operator in the Left Join Condition. How this is currently used is in a stored procedure that takes values from a C# application in the form of an XML variable. The XML Variable is placed into table #valTest. All columns are of datatype INT. If vt.Response is valid for vaRule, the result of & will be greater than zero. (i.e. 31 & 8 = 8 but 12 & 2 = 0). vt.Test column contains the number 1 for each row, so that it may be summed up (nulls are automatically excluded) to get a count of the validations that pass by rule. Each rule has a number of attributes that must pass validation for success. If the number of attributes is equal to those that passed, we have success.
In an effort to reduce calls to the database, the goal is to cache ALL the rules in the ASP.NET cache and handle validation localy. The developers are asking for a de-normalized version of the validation data with the claim that the SQL Set based operation is not a simple task in C# with Linq. From what I have looked into, I would agree. At this point my investigation shows the bitwise comparison in the join condition is particularly problematic.
The main question is how is can this be converted to something that uses Linq on the C# side? Or, are there more efficient ways to deal with this on the client side and Linq is not one of them (i.e. just give them flat data)?
thanks
LINQ-to-SQL isn't going to do anything quite as bespoke as that query. Which isn't a criticism of either LINQ-to-SQL or the query: simply, there are limits.
There are two ways I would approach that:
1: as a parameterized TSQL query via ExecuteQuery<T> - i.e.
var result = db.ExecuteQuery<YourType>(#"your query here with {0}, {1} etc",
arg0, arg1, ...);
2: write that TSQL a udf mapped into the data-context:
var result = db.YourUdf(arg0, ...);
Both are valid and will work with LINQ-to-SQL; personally I prefer the first approach, but the UDF approach allows greater re-use within the DB layer, at the expense of having more complex deployment (i.e. app tier and db tier all at the same time).