Why is my 'WHERE' clause based on a 'DATE' failing? - sql

I'm using node.js to connect to an SQL database (SQL Server 2016 specifically). My table, called transactionCounts, has the following tables and data types:
staff_id: varchar(50), date: Date, count: int;
The 'date' field is just a date, not a DateTime, for clarity. Records look like this: "2017-08-07"
I'm using the mssql package, const sql = require('mssql');
So basically, I have a function which accepts a start date and an end date and does this with them:
function(start, end) {
let ps = new sql.PreparedStatement(transactionRegisterSqlPool);
ps.input('start', sql.Date);
ps.input('end', sql.Date);
ps.prepare('SELECT staff_id, SUM(Count) TotalCount FROM [TransactionRegister].[dbo].[transactionCounts] ' +
'WHERE date >= #start AND date < #end GROUP BY staff_id', err => {
.execute({start: start, end: end}, (err, result) => {});
});
};
I've simplified the function for illustrations sake (it normally returns a promise), but here's what's going wrong:
I pass in the dates of Aug 20 midnight, and Aug 27 midnight, and what I'm expecting to get back is the sum for the dates 20,21,22,23,24,25 and 26 naturally (7 days, a week).
26th isn't being included though (definitely), and I'm not entirely sure but I'd wager that the 19th is being included. I think it's a daylight-savings issue, because these dates, when i call .toISOString(), look like 2017-08-19T23:00:00.000Z and 2017-08-26T23:00:00.000Z respectively (11pm the night prior).
I've modified my function to use strings instead of dates, and this seems to work and returns the correct Sums:
function(start, end) {
let ps = new sql.PreparedStatement(transactionRegisterSqlPool);
ps.input('start', sql.VarChar);
ps.input('end', sql.VarChar);
start = `${start.getFullYear()}/${start.getMonth() + 1}/${start.getDate()}`;
end = `${end.getFullYear()}/${end.getMonth() + 1}/${end.getDate()}`;
ps.prepare('SELECT staff_id, SUM(Count) TotalCount FROM [TransactionRegister].[dbo].[transactionCounts] ' +
'WHERE date >= #start AND date < #end GROUP BY staff_id', err => {
ps.execute({start: start, end: end}, (err, result) => {});
});
};
But it seems...wrong to take my dates and turn them into strings to work my way around this issue. What is the correct way to deal with dates between Javascript Dates and SQL Dates, so that this apparent Daylight-Savings-caused issue is avoided?

Your problem is that JavaScript does not have a "date" type, only "datetime", yet SQL does have a "date" type. Because of that, you will have to do the conversion.
If you wrap it in a function, it is still readable:
function toDateString(d) {
return `${d.getFullYear()}/${d.getMonth() + 1}/${d.getDate()}`;
}
ps.prepare('SELECT staff_id, SUM(Count) TotalCount FROM [TransactionRegister].[dbo].[transactionCounts] ' +
'WHERE date >= #start AND date < #end GROUP BY staff_id', err => {
ps.execute({start: toDateString(start), end: toDateString(end)}, (err, result) => {});
});

Related

select, group, sum with ASP LINQ + private compare method

i am working on an ASP MVC Project and i have a part where i need to export some data to an excel file. the thing is that i need to sum some rows to display a single row. I am not familiar with sql or linq and i am struggling to get the result i want.
there should be 4 columns: requester(string), date(datetime), collection(string), timeinvested(int).
the grouping should be by the column 'collection' (string) as default and if the filter input for requester or date was filled than that would be the next level of grouping. the column 'timespent' should be summarized by the result of the grouping.
example:
requester, date, collection, timeinvested
(1) john, jan 1st, 2019, collection1, 1
(2) mike, jan 1st, 2019, collection1, 3
(3) eric, jan 1st, 2019, collection1, 2
(4) july, jan 1st, 2019, collection2, 5
(5) john, jan 1st, 2012, collection1, 3
here we have 5 rows from the table, once we filter to export only by default (column collection) then rows 1+2+3+5 should sum to 1 row and the 2nd row will be row 4, because the collections are different. like so:
requester, date, collection, timeinvested
(1) john, jan 1st, 2019, collection1, 9
(2) july, jan 1st, 2019, collection2, 5
if i choose to filter also by requester or date then it should apply accordingly.
one big thing here is that there is a private method that checks the date to a particular date and it should export to the file the date year if its after or before the checked date. for example: relative date dec 1st of the same year
if the row date is before relative date then we should write date year - 1;
thanks
var q = (from a in items
select new {
Requester = a.Requester.Name,
Collection = a.Collection.Name,
TimeSpent = a.TimeSpent,
Year = a.Date,
})
.ToList()
.GroupBy(x => new {
x.Requester,
x.Collection,
x.Year,
x.TimeSpent
})
.Select(y => new CollectionModel {
Requester = y.Key.Requester,
Collection = y.Key.Collection,
TimeSpent = y.Sum(z => Convert.ToInt32(z.TimeSpent)),
Year = checkDate(y.Key.Year),
});
return q.ToList();
this is how i made it work:
var q = (from a in items
select new {
Requester = a.Requester.Name,
Collection = a.Collection.Name,
TimeSpent = a.TimeSpent,
Year = a.Date,
})
.Where(Where(a => dt == null || (dt != null && a.Year == dt.Year))
.ToList()
.GroupBy(x => new {
x.Requester,
x.Collection,
x.Year,
})
.Select(y => new CollectionModel {
Requester = y.Key.Requester,
Collection = y.Key.Collection,
TimeSpent = y.Sum(z => Convert.ToInt32(z.TimeSpent)),
Year = checkDate(y.Key.Year),
});
return q.ToList();
First, remove the "x.TimeSpent" from your GroupBy, you need to aggregate that variable, not group by it. secondly, could you post the output as well?

use field as recursive function in where condition in yii2

I want to send content of date1 to exp function in where() condition and return the result.
Note: Actually I want to compare two date, that I need to change one of dates to explode
Here is my code:
function exp($date){
$date = explode('/', $date);
$Y = $date[0];
$m = $date[1];
$d = $date[2];
return $Y;
}
$promise = new ActiveDataProvider([
'query' => Post::find()
->where('status = "show"')
->andWhere(['<=', exp('date1'), 'date2'])// date1 is: 2018/02/03
->orderBy('id'),
]);
Is there any way else to do this?
exp is a PHP function, in andWhere you prepare an SQL query that will run on the SQL server. The SQL parser on the SQL server can not execute PHP functions. You should use MySQL DATE_FORMAT function here:
$promise = new ActiveDataProvider([
'query' => Post::find()
->where('status = "show"')
->andWhere(['<=', 'DATE_FORMAT(date1, "%Y")', 'date2'])
->orderBy('id'),
]);
Please change the name of the fields date1 and date2 to make them more informative. For ex. order_date, delivery_date etc.
If status values are often used in your code, you should replace them with a constants of the Postclass.
If date2 contains only year values, it is better to use the YEAR type.

SQL to Linq group by count

I have a single column in a table to count specific rows. The sql query is as below:
SELECT
CASE
WHEN trail LIKE 'ClassA%' THEN 'ClassA'
WHEN trail LIKE 'ClassB%' THEN 'ClassB'
WHEN trail LIKE 'SemA%' THEN 'SemesterA'
WHEN trail LIKE 'SemB%' THEN 'SemesterB'
END AS Logs
, COUNT(*) AS Count
FROM Logs where s_date >= 'from date from UI' and e_date <= 'to date from ui'
GROUP BY
CASE
WHEN trail LIKE 'ClassA%' THEN 'ClassA'
WHEN trail LIKE 'ClassB%' THEN 'ClassB'
WHEN trail LIKE 'SemA%' THEN 'SemesterA'
WHEN trail LIKE 'SemB%' THEN 'SemesterB'
END
The above query result in sql fine as
ClassA 20
ClassB 5
SemesterA 2
SemesterB 50
Now, I need to change this sql to Linq with a date filter (from date, to date).
Please suggest change in query to simplyfy it.
Thanks
Tried:-
var data = _db.Logs.Where(p => p.trail.StartsWith("ClassA") && (p.SDate.Date >= CDate.Date && p.SDate.Date <= FDate.Date)).GroupBy(p => p.trail.StartsWith("ClassA")).Select(s =>
new
{
source = "Class - A total",
percentage = s.Count()
}).Union(_db.Logs.Where(p => p.trail.StartsWith("ClassB") && (p.SDate.Date >= CDate.Date && p.SDate.Date <= FDate.Date)).GroupBy(p => p.trail.StartsWith("ClassB")).Select(s =>
new
{
source = "Class - B total",
percentage = s.Count()
}).Union(_db.Logs.Where(p => p.trail.StartsWith("SemesterA") && (p.SDate.Date >= CDate.Date && p.SDate.Date <= FDate.Date)).GroupBy(p => p.trail.StartsWith("SemesterA")).Select(s =>
new
{
source = "Semester - A total",
percentage = s.Count()
}).Union(_db.Logs.Where(p => p.trail.StartsWith("SemesterB") && (p.SDate.Date >= CDate.Date && p.SDate.Date <= FDate.Date)).GroupBy(p => p.trail.StartsWith("SemesterB")).Select(s =>
new
{
source = "Semester - B total",
percentage = s.Count()
})))).ToList();
Try storing all the interesting starting keys in an enumerable of some sort and then using the built in group by method overload which outputs a result mapped from the key,group pairs (c.f. https://msdn.microsoft.com/en-us/library/vstudio/bb549393(v=vs.100).aspx)
string[] startingKeys = new string[] {"ClassA","ClassB","SemsterA","SemesterB"};
var data =_db.Logs.Where(p=>(p.SDate.Date >= CDate.Date && p.SDate.Date <= FDate.Date)&&startingKeys.Any(k=>p.Logs.StartsWith(k))).GroupBy(p=>startingKeys.Where(k=>p.Logs.StartsWith(k)).First(),(key,items)=>new {source=key,count = items.Count()})
One advantage of this method is you can change the starting keys at runtime if you feel like it.

Convert SQL to LINQ with group by

I'm stumped trying to convert the following sql to linq:
SELECT t.* FROM(SELECT mwfieldid,MAX([TimeStamp]) AS MaxValue, BatchDocumentID
FROM mw_BatchField
GROUP BY mwfieldid,BatchDocumentID) x
JOIN mw_BatchField t ON x.mwfieldid = t.mwfieldid
AND x.MaxValue = t.TimeStamp
and x.BatchDocumentID = t.BatchDocumentID
So far I had to convert it to a stored proc to get it to work. I'd rather know how to write this correctly in linq. I tried using a sql to linq converter (http://www.sqltolinq.com/) which produced this code that had errors in it: (Are these converters any good? It didn't seem to produce anything useful with a few tries.)
From x In (
(From mw_BatchFields In db.mw_BatchFields
Group mw_BatchFields By
mw_BatchFields.MWFieldID,
mw_BatchFields.BatchDocumentID
Into g = Group
Select
MWFieldID,
MaxValue = CType(g.Max(Function(p) p.TimeStamp),DateTime?),
BatchDocumentID)
)
Join t In db.mw_BatchFields
On New With { .MWFieldID = CInt(x.MWFieldID), .MaxValue = CDate(x.MaxValue), .BatchDocumentID = CInt(x.BatchDocumentID) }
Equals New With { .MWFieldID = t.MWFieldID, .MaxValue = t.TimeStamp, .BatchDocumentID = t.BatchDocumentID }
Select
BatchFieldID = t.BatchFieldID,
BatchDocumentID = t.BatchDocumentID,
MWFieldID = t.MWFieldID,
TimeStamp = t.TimeStamp,
value = t.value,
DictionaryValue = t.DictionaryValue,
AutoFilled = t.AutoFilled,
employeeID = t.employeeID
Seems like a lot of code for such a simple query, and it doesn't compile.
So for every combination of mwfieldid and BatchDocumentID you want all columns of the row with the highest TimeStamp? This is something which is much easier to express in LINQ than SQL so I'm not surprised that an automated converter is making a meal of it.
You should be able to do:
Mw_BatchFields.GroupBy(x => new { x.Mwfieldid, x.BatchDocumentId })
.SelectMany(x => x.Where(y => y.TimeStamp == x.Max(z => z.TimeStamp)))
This (like your SQL) will return multiple rows per grouping key if there is more than one row in the group that shares the same maximum TimeStamp. If you only want row per key, you could use:
Mw_BatchFields.GroupBy(x => new { x.Mwfieldid, x.BatchDocumentId })
.Select(x => x.OrderByDescending(y => y.TimeStamp).First())
Edit:
Sorry, just twigged that you're working in VB, not C#, so not quite what you were looking for, but if you can live with the lambda syntax style, I think the above can be translated as:
Mw_BatchFields.GroupBy(Function(x) New With {x.Mwfieldid, x.BatchDocumentId}).Select(Function(x) x.OrderByDescending(Function(y) y.TimeStamp).First())
and:
Mw_BatchFields.GroupBy(Function(x) New With {x.Mwfieldid, x.BatchDocumentId}).SelectMany(Function(x) x.Where(Function(y) y.TimeStamp = x.Max(Function(z) z.TimeStamp)))

LINQ to Entities does not recognize the method 'System.DateTime ToDateTime(System.String)' method

Hello This is my first post here
I'm facing some issue with EF , I have tried the following code in VS 2010 with EF 5 and 6.
I have table called MYTABLE and field ReveivedTime which varchar(255) but this has Datetime values like "4/29/2014 12:00:00 AM".
Note : I can not change the DataType and I can not write SP to Convert , I do have limitations, So I do have option to work with LAMBDA
I have tired the following code in LINQPad 4 itis working fine but the same is not working in VS2010 with EF 5/6.
getting error
LINQ to Entities does not recognize the method 'System.DateTime ToDateTime(System.String)' method, and this method cannot be translated into a store expression.
Convert.ToDateTime(si.ReceivedTime) is causing the problem
MYTable
.Where (
si =>
((Convert.ToDateTime(si.ReceivedTime) >= DateTime.Now.AddDays(-10)) &&
(Convert.ToDateTime (si.ReceivedTime) <= DateTime.Now)
)
)
.GroupBy (g => new
{
Day = Convert.ToDateTime(g.ReceivedTime).Day,
Month = Convert.ToDateTime(g.ReceivedTime).Month,
Year = Convert.ToDateTime(g.ReceivedTime).Year,
City = g.City,
})
.Select (
g =>
new
{
Count = g.Count(),
g.Key.Day,
g.Key.Month,
g.Key.Year,
g.Key.City,
Date = Convert.ToDateTime(g.Key.Year + "/" + g.Key.Month + "/" + g.Key.Day),
}
)
.OrderBy(x => x.Count)
Really appreciate your solution and advanced thanks too.
Instead of Convert.ToDateTime (si.ReceivedTime) <= DateTime.Now try SqlFunctions.DateDiff("s", si.ReceivedTime, DateTime.Now) > 0