I have the following dataset for a TimeTable that needs to be displayed in a gridview. Currently a snippet of the dataset looks like this:
SessionNum TimeStart TimeStop Details
---------- --------- -------- -------
1 08:00 09:00 Math101
1 09:00 10:00 Comp102
1 11:00 12:00 Engn101
2 08:00 09:00 Comp102
2 09:00 10:00 Math101
2 10:00 11:00 Acco103
There are a total of 5 sessions, and I would like for the dataset to look like:
TimeStart TimeStop Session1 Session2 ...
--------- -------- -------- -------- ---
08:00 09:00 Math101 Comp102
09:00 10:00 Comp102 Math101
10:00 11:00 - Acco103
11:00 12:00 Engn101 -
As you will see, there are no aggregate functions required...just grouping, but for the life of me I cannot seem to wrap my head around this one. I have the following LINQ query which generates the first dataset:
List<TimeTable> list = db.TimeTables.OrderBy(o => o.TimeStart).OrderBy(o => o.SessionNum).ToList();
This works fine, and generates the dataset sorted by SessionNum and then TimeStart. My attempt to solve this invlovled the following query:
var result = list.GroupBy(t => t.TimeStart).Select(s => new {
TimeStart = s.Key,
Session1 = s.Where(x => x.SessionNum == 1),
Session2 = s.Where(x => x.SessionNum == 2)
});
This ran, but unfortunately did not work. I know a GroupBy (or a couple) is/are required, but I'm a bit lost from this point forward. I would really appreciate any help towards solving this. Thank you in advance!
You can't directly do a pivot query in LINQ. What you can do instead is create a structure like this:
var record = new
{
TimeStart = "10:00",
TimeStop = "11:00",
Sessions = new [] { "-", "Acco103", },
};
When you have a list of these records you must ensure that the Sessions property is array that is the same length as the distinct number of sessions in your entire set of data. Then you can access the session information by indexing into the array.
This should make more sense after looking at the queries.
First, query the database for the required data:
var query =
from s in db.TimeTables
orderby s.TimeStop
orderby s.TimeStart
group s by new { s.TimeStart, s.TimeStop } into gss
select new
{
gss.Key.TimeStart,
gss.Key.TimeStop,
Sessions = gss.ToArray(),
};
Now determine the distinct set of sessions:
var sessionNums =
db.TimeTables
.Select(s => s.SessionNum)
.Distinct()
.OrderBy(n => n)
.ToArray();
Now process this data in memory (note the .ToArray() call on query):
var process =
from q in query.ToArray()
let lookup = q.Sessions
.ToLookup(s => s.SessionNum, s => s.Details)
select new
{
q.TimeStart,
q.TimeStop,
Sessions = sessionNums
.Select(n => String.Join(
", ",
lookup[n].DefaultIfEmpty("-")))
.ToArray(),
};
This is where the hard work is. The lookup creates an easy way to get session detail out for any SessionNum. Calling lookup[n].DefaultIfEmpty("-") ensures that there is at least a single value for each session. The String.Join ensures that if the source data had two sessions for the same session number at the same time that we end up with one value.
This result is safe no matter how many sessions there are as it will just extend the arrays.
The output of the process query looks like this:
Then you can do this query:
var result =
from p in process
select new
{
p.TimeStart,
p.TimeStop,
Session1 = p.Sessions[0],
Session2 = p.Sessions[1],
};
This will effectively "pivot" your results, but you need to explicitly put in each "SessionX" property.
The output of the result query looks like this:
Related
I have a Model Charter that hasMany BlackoutRanges. I am trying to setup a scope to return charters that do not have a blackoutRange created for 3 dates. In our system a blackoutRange is a single day.
Pseudocode would look something like this:
// query where doesnt have blackoutDates on all three of the following dates:
// start date === date passed in
// start date === date passed in plus 1 days
// start date === date passed in plus 2 days
I tried to do some logical grouping and came up with this and I also logged my raw sql query to see what it looks like:
$dayTwo = $date->copy()->addDays(1);
$dayThree = $date->copy()->addDays(2);
return $query->whereDoesntHave("blackoutRanges", function(Builder $subQuery) use($date, $dayTwo, $dayThree){
$temp = $subQuery->where(function($queryOne) use($date) {
return $queryOne->whereDate('start', $date)->where('trip_id', NULL);
})->where(function($queryTwo) use($dayTwo) {
return $queryTwo->whereDate('start', $dayTwo)->where('trip_id', NULL);
})->where(function($queryThree) use($dayThree) {
return $queryThree->whereDate('start', $dayThree)->where('trip_id', NULL);
});
logger($temp->toSql());
return $temp;
});
select * from `blackout_ranges` where `charters`.`id` = `blackout_ranges`.`charter_id` and (date(`start`) = ? and `trip_id` is null) and (date(`start`) = ? and `trip_id` is null) and (date(`start`) = ? and `trip_id` is null)
I've also logged the dates passed in and they are:
[2022-06-07 19:00:58] local.DEBUG: 2022-06-09 00:00:00
[2022-06-07 19:00:58] local.DEBUG: 2022-06-10 00:00:00
[2022-06-07 19:00:58] local.DEBUG: 2022-06-11 00:00:00
An example of the start column of a BlackoutRange would be: 2022-07-21 04:00:00
Would the fact that they are not matching timestamps be a reason for this scope not working?
And how do I know it's not working? I added blackoutRanges for a specific charter for the entire month of June and it's still being returned.
I am looking for any advice on this one as I've been plugging away now for a day and a half and have gotten no where.
Is there a way to write a MySQL query to get records by monthly for example:
Starting from the current month:
----- Jan -----
Record 1
Record 2
----- Feb -----
Record 1
Record 2
----- March-----
Record 1
Record 2
I have starting and ending timestamp and start_date, end_date(MM/DD/YYYY format) column in my db table.
solved by getting start date & end date of month
$month_start_time = strtotime(date('Y-m-01', $timestamp)." 00:00:00");
$month_end_time = strtotime(date('Y-m-t', $timestamp)." 23:59:59");
$sdate = date('d', $month_start_time);
$edate = date('d', $month_end_time);
$j=0;
for($k=$sdate; $k <= $edate; $k++){
$start_date = $k.date('/m/Y', $month_start_time);
$stimestamp = strtotime(date('Y/m', $month_start_time).'/'.$k." 00:00:00");
$end_date = strtotime(date('Y/m', $month_start_time).'/'.$k." 23:59:59");
//then pass in mysql query
}
I have the following code and would like to convert the request into a mysql query. Right now I achieve the desired result using a manual .select (array method) on the data. This should be possibile with a single query (correct me if I am wrong).
Current code:
def self.active_companies(zip_code = nil)
if !zip_code
query = Company.locatable.not_deleted
else
query = Company.locatable.not_deleted.where("zip_code = ?", zip_code)
end
query.select do |company|
company.company_active?
end
end
# Check if the company can be considered as active
def company_active?(min_orders = 5, last_order_days_ago = 15)
if orders.count >= min_orders &&
orders.last.created_at >= last_order_days_ago.days.ago &&
active
return true
else
return false
end
end
Explanation:
I want to find out which companies are active. We have a company model and an orders model.
Data:
Company:
active
orders (associated orders)
Orders:
created_at
I don't know if it is possible to make the company_active? predicate a single SQL query, but I can offer an alternative:
If you do:
query = Company.locatable.not_deleted.includes(:orders)
All of the relevant orders will be loaded into the memory for future processing.
This will eliminate all the queries except for 2:
One to get the companies, and one to get all their associated orders.
I'm getting a weird return when executing this query :
SELECT * FROM rrp
WHERE end > "2012-12-31"
nothing is returned, although I have one row on this table which "end" column is greater than "2012-12-31":
rrp
id_r | id__b | start | end | quantity
27 29 2012-01-01 2012-05-05 1
31 29 2012-11-01 2013-01-01 1
EDIT : startand endare date fields
EDIT : I used wrong database for my tests => wrong result
the issue was coming from Zend_Date when adding a day to a date:
$start = "2012-12-31";
$nStart = new Zend_Date($start, "YYYY-MM-dd");
$end = new Zend_Date($nStart);
$end->addDay(1);
When i echoed $end : echo $end->get("YYYY-MM-dd");
it outputs 2013-12-31
Most likely an issue with how the dates are formatted
This should help
http://dev.mysql.com/doc/refman/5.0/en/using-date.html
If end is a DATE column, it should work as expected:
SELECT
STR_TO_DATE('2013-01-01', '%Y-%m-%d') < "2012-12-31",
STR_TO_DATE('2012-05-05', '%Y-%m-%d') < "2012-12-31"
... returns 0, 1 in my box.
The only possible flaw I can think of is that your system's default date format is not %Y-%m-%d:
SELECT ##DATE_FORMAT
In that case, you need to specify a format every time:
SELECT *
FROM rrp
WHERE end > STR_TO_DATE('2012-12-31', '%Y-%m-%d')
So similar questions have been asked with not much of an answer....
I have a Stats Table related to a Branch table. The Stats records contain pc stats of a particular bank branch.
Branch
+Code
+Name
+Area
...
Stats
+BranchCode
+IP
+UpSpeed
+DownSpeed
...
Here is my linq query...
var stats = from st in store.Stats
join b in store.Branches on st.BranchCode equals b.Brcd
select new
{
st.ID,st.IP,st.Name,b.Brcd,b.Branch_Name..............
};
The issue is st and b have a LOT of fields, for now I guess I will type them all... but isn't there a solution to this? *Prevent the typing of all fields... something like a * wildcard?
Did try intersect however the types need to be the same!
Thanks
Gideon
1
var stats =
from st in store.Stats
join b in store.Branches on st.BranchCode equals b.Brcd
select new
{
Stats = st,
Branch = b
};
Creates anonymous instances with one Stats and one Branch.
2
var stats =
from b in store.Branches
join st in store.Stats
on b.Brcd equals st.BranchCode
into branchstats
select new
{
Branch = b
Stats = branchstats
};
Creates anonymous instances with one Branch and its Stats.
3
var stats =
from b in store.Branches
select new
{
Branch = b
Stats = b.Stats
};
Same as 2, If there's an association between the two types in the designer, then there's a relational property generated on each type.
4
DataLoadOptions dlo = new DataLoadOptions()
dlo.LoadWith<Branch>(b => b.Stats);
store.LoadOptions = dlo;
List<Branch> branches = store.Branches.ToList();
Here, DataLoadOptions are specified that automatically populate the Stats property when any Branch is loaded.