I cannot rewrite the current DB schema right now BTW, but that's beside the point to the issue I've hit, so please ignore the table structure :D
I'm running this DB query:
my $rs = $dbx->resultset('Result')->search(
{
'result_hnd' => 16078055,
'seasons.outdoor' => 'venue.outdoors',
'seasons.start_date' => { '<=' => 'meet.date_end' },
'seasons.end_date' => { '>=' => 'meet.date_begin' },
},
{
'join' => [
{
'team' => {
'league_teams' => {
'league' => 'seasons',
},
},
},
{
'meet' => 'venue'
},
],
'+select' => ['seasons.season_hnd','seasons.name','seasons.start_date','seasons.end_date','meet.date_begin','meet.date_end'],
'+as' => ['season_hnd','season_name','s_start','s_end','m_start','m_end'],
columns => ['result_hnd'],
group_by => ['seasons.season_hnd'],
}
);
When I run this, I get no results. With DBIC_TRACE on, I see the generated SQL as:
SELECT me.result_hnd, seasons.season_hnd, seasons.name, seasons.start_date, seasons.end_date, meet.date_begin, meet.date_end FROM track.result me JOIN track.team team ON team.team_hnd = me.team_hnd LEFT JOIN track.league_team league_teams ON league_teams.team_hnd = team.team_hnd LEFT JOIN track.league league ON league.league_hnd = league_teams.league_hnd LEFT JOIN track.season seasons ON seasons.league_hnd = league.league_hnd OR seasons.league_hnd = league.parent_league_hnd JOIN track.meet meet ON meet.meet_hnd = me.meet_hnd JOIN track.venue venue ON venue.venue_hnd = meet.venue_hnd WHERE ( ( result_hnd = ? AND seasons.end_date >= ? AND seasons.outdoor = ? AND seasons.start_date <= ? ) ) GROUP BY seasons.season_hnd: '16078055', 'meet.date_begin', 'venue.outdoors', 'meet.date_end'
When I copy and paste this statement into my MYSQL client (and interpolate the placeholders), like this:
SELECT me.result_hnd, seasons.season_hnd, seasons.name, seasons.start_date, seasons.end_date, meet.date_begin, meet.date_end
FROM track.result me
JOIN track.team team ON team.team_hnd = me.team_hnd
LEFT JOIN track.league_team league_teams ON league_teams.team_hnd = team.team_hnd
LEFT JOIN track.league league ON league.league_hnd = league_teams.league_hnd
LEFT JOIN track.season seasons ON seasons.league_hnd = league.league_hnd OR seasons.league_hnd = league.parent_league_hnd
JOIN track.meet meet ON meet.meet_hnd = me.meet_hnd
JOIN track.venue venue ON venue.venue_hnd = meet.venue_hnd
WHERE ( ( result_hnd = 16078055 AND seasons.end_date >= meet.date_begin AND seasons.outdoor = venue.outdoors AND seasons.start_date <= meet.date_end ) )
GROUP BY season_hnd;
I get the exact result I expect (7 records).
This is really bizarre. To all intents and purposes, isn't that exactly the same query? Am I missing something in my debugging? Or is something else happening at the DBIx::Class::ResultSet layer that isn't being dumped?
To tell SQL::Abstract that the value on the right is actually an identifier, you can do the following (as outlined in the docs):
{
'result_hnd' => 16078055,
'seasons.outdoor' => { -ident => 'venue.outdoors' },
'seasons.start_date' => { '<=' => { -ident => 'meet.date_end' } },
'seasons.end_date' => { '>=' => { -ident => 'meet.date_begin' } },
},
To all intents and purposes, isn't that exactly the same query?
(For all intents and purposes, ...)
No. In the log, you have something equivalent to
result_hnd = '16078055'
AND seasons.end_date >= 'meet.date_begin'
AND seasons.outdoor = 'venue.outdoors'
AND seasons.start_date <= 'meet.date_end'
or maybe
result_hnd = 16078055
AND seasons.end_date >= 'meet.date_begin'
AND seasons.outdoor = 'venue.outdoors'
AND seasons.start_date <= 'meet.date_end'
In your attempt, you used
result_hnd = 16078055
AND seasons.end_date >= meet.date_begin
AND seasons.outdoor = venue.outdoors
AND seasons.start_date <= meet.date_end
Sorry, I don't have the solution for you.
Related
Sorry for my English.
I ran into a mistake with EF Core 5.0.9 when is use Filtered Include and Select in same time and i don't know is a bug or feature. :)
return await _dbContext.User
.Where(u => !u.TOROLT)
.Where(u => ids.Contains(u.Id))
.Include(u => u.EventUsers.Where(eu => !eu.TOROLT && eu.EventId == eventId))
.Select(u => new UserDropDownDtoWithInviteData
{
Id = u.Id,
FirstName = u.FirstName,
LastName = u.LastName,
EventUserId = u.EventUsers.First().Id,
IsCelebrated = u.EventUsers.First().IsCelebrated,
IsEventAdmin = u.EventUsers.First().IsEventAdmin,
IsInviteAccepted = u.EventUsers.First().IsInviteAccepted,
IsInvited = u.EventUsers.First().IsInvited,
})
.ToListAsync();
In this time the first elements is select is not from filtered include just from a normal include. SQL Script from Profiler:
SELECT
[u].[Id],
[u].[FirstName],
[u].[LastName],
(
SELECT TOP(1) [e].[Id]
FROM [dbo].[EventUser] AS [e]
WHERE [u].[Id] = [e].[UserId]) AS [EventUserId],
(
SELECT TOP(1) [e0].[IsCelebrated]
FROM [dbo].[EventUser] AS [e0]
WHERE [u].[Id] = [e0].[UserId]) AS [IsCelebrated],
(
SELECT TOP(1) [e1].[IsEventAdmin]
FROM [dbo].[EventUser] AS [e1]
WHERE [u].[Id] = [e1].[UserId]) AS [IsEventAdmin],
(
SELECT TOP(1) [e2].[IsInviteAccepted]
FROM [dbo].[EventUser] AS [e2]
WHERE [u].[Id] = [e2].[UserId]) AS [IsInviteAccepted],
(
SELECT TOP(1) [e3].[IsInvited]
FROM [dbo].[EventUser] AS [e3]
WHERE [u].[Id] = [e3].[UserId]) AS [IsInvited]
FROM [dbo].[User] AS [u]
WHERE ([u].[TOROLT] <> CAST(1 AS bit))
AND [u].[Id] IN (2, 1, 3, 4, 5)
But if is separate filtered include and select, than it work's fine, but this select complete record and not the part of him from database:
var a = await _dbContext.User
.Where(u => !u.TOROLT)
.Where(u => ids.Contains(u.Id))
.Include(u => u.EventUsers.Where(eu => !eu.TOROLT && eu.EventId == eventId))
.ToListAsync();
return a.Select(u => new UserDropDownDtoWithInviteData
{
Id = u.Id,
FirstName = u.FirstName,
LastName = u.LastName,
EventUserId = u.EventUsers.First().Id,
IsCelebrated = u.EventUsers.First().IsCelebrated,
IsEventAdmin = u.EventUsers.First().IsEventAdmin,
IsInviteAccepted = u.EventUsers.First().IsInviteAccepted,
IsInvited = u.EventUsers.First().IsInvited,
})
.ToList();
Any idea why is this, and how can i solve solve this?
THX
Include is completely ignored if you have custom projection Select, so your filter will be also ignored. It is not a bug, Include works only when you get whole entity from query.
Anyway consider to rewrite your query:
var query =
from u in _dbContext.User
where !u.TOROLT && ids.Contains(u.Id)
from eu in u.EventUsers.Where(eu => !eu.TOROLT && eu.EventId == eventId)
.Take(1)
.DefaultIfEmpty()
select new UserDropDownDtoWithInviteData
{
Id = u.Id,
FirstName = u.FirstName,
LastName = u.LastName,
EventUserId = eu.Id,
IsCelebrated = eu.IsCelebrated,
IsEventAdmin = eu.IsEventAdmin,
IsInviteAccepted = eu.IsInviteAccepted,
IsInvited = eu.IsInvited,
};
var result = await query.ToListAsync();
If possible, please guide me how to convert my SQL command to lambda expression
select d.* from takcontractdet d where d.contractid=pubcontractid and d.personid in
(
select p.id from personel p where p.parentid=pubparentid)
)
Two query
var personelIds = Personels.Where(p => p.ParentId == pubparentid)
.Select(e => e.Id).ToList();
var result = Takcontractdets.Where(d => d.contractid == pubcontractid &&
personelIds.Contains(d.PersonId)).ToList();
One query
var result = Takcontractdets.Where(d => d.contractid == pubcontractid &&
Personels.Where(p => p.ParentId == pubparentid)
.Select(e => e.Id).ToList().Contains(d.PersonId))
.ToList();
I use a MySQL database in production and a SQLite database for running tests. One part of my application is used to gather monthly statistics for a year. I've successfully done this, however it came at a cost of not being able to automate tests because I'm using MySQL specific functions when querying for the data:
my $criteria = {
status => ['Complete'],
'YEAR(completed_on)' => DateTime->now()->year(),
};
my $attributes = {
select => [ { count => 'title' }, 'completed_on' ],
as => [qw/num_completed datetime/],
group_by => [qw/MONTH(completed_on)/],
};
Notice I'm using YEAR and MONTH MySQL functions.
I know one way I can substitute the where clause to eliminate the use of MySQLs YEAR function, something like this:
my $dtf = $schema->storage->datetime_parser;
my $begin_date = DateTime->from_day_of_year( year => DateTime->now()->year(), day_of_year => 1 ); #inception o_O
my $end_date = DateTime->from_day_of_year( year => DateTime->now()->year(), day_of_year => 36[56] );
my $criteria = {
status => ['Complete'],
completed_on =>
-between => [
$dtf->format_datetime($begin_date),
$dtf->format_datetime($end_date),
]
};
Using the recommended way to query date fields using DBIC
But I'm stumped as to what to do with the group_by clause and how to make the grouping of this fields date value by month database agnostic as well. Wondering if anyone has any ideas?
Thanks!
Sometimes you will have to make engine specific code in DBIx::Class if you're trying to do special things. You can use $schema->storage->sqlt_type to make different SQL.
Note you can also use substr(completed_on,1,4) to get the year in SQLite.
This will solve your problem:
my $type = $schema->storage->sqlt_type;
my $criteria;
my $attributes;
if ($type eq 'MySQL') {
$criteria = {
status => ['Complete'],
'YEAR(completed_on)' => DateTime->now()->year(),
};
$attributes = {
select => [ { count => 'title' }, 'completed_on' ],
as => [qw/num_completed datetime/],
group_by => [qw/MONTH(completed_on)/],
};
}
elsif ($type eq 'SQLite') {
my $dtf = $schema->storage->datetime_parser;
my $begin_date = DateTime->from_day_of_year( year => DateTime->now()->year(), day_of_year => 1 ); #inception o_O
my $end_date = DateTime->from_day_of_year( year => DateTime->now()->year() + 1, day_of_year => 1 )->add( seconds => -1 );
$criteria = {
status => ['Complete'],
completed_on => {
-between => [
$dtf->format_datetime($begin_date),
$dtf->format_datetime($end_date),
]
}
};
$attributes = {
select => [ { count => 'title' }, 'completed_on' ],
as => [qw/num_completed datetime/],
group_by => ['substr(completed_on,6,2)'],
};
}
I got a huge problem with the SQL generation in my REST-Service:
The following Code is generating 2 different SQL-Statements (PC Win8 vs Server 2008 R2):
private void GetData()
{
ctPostBox = new CTPostBox(GetDBConnection(DatabaseKind.dkGlobal));
var lQry = (from d in ctPostBox.CONTACTUSER.Include("CONTACT").Include("CONTACT.DBLINK").Include("CONTACT.CONTACTLASTUSED").Include("CONTACT.EMAILACCOUNT")
where ((d.CONTACT.ID_DBLINK == 0 || d.CONTACT.ID_DBLINK == null) || (d.CONTACT.DBLINK.MANDNR == UserConfig.mandNr))
select new
{
READDATE = d.READDATE,
ID = d.CONTACT.ID,
DBID = d.CONTACT.DBID,
DATEINSERT = d.CONTACT.DATEINSERT,
FROMUSER = d.CONTACT.FROMUSER,
FILENAME = d.CONTACT.FILENAME,
KIND = d.CONTACT.KIND,
MSGID = d.CONTACT.MSGID,
ID_DBLINK = d.CONTACT.ID_DBLINK,
DBID_DBLINK = d.CONTACT.DBID_DBLINK,
SUBJECT = d.CONTACT.SUBJECT,
STATE = d.CONTACT.STATE,
DATEINCOME = d.CONTACT.DATEINCOME,
DATESEND = d.CONTACT.DATESEND,
ID_EMAILACCOUNT = d.CONTACT.ID_EMAILACCOUNT,
DBID_EMAILACCOUNT = d.CONTACT.DBID_EMAILACCOUNT,
GESCHST = d.CONTACT.DBLINK.GESCHST,
d.CONTACT.DBLINK.KEYTYP,
KONTO = d.CONTACT.EMAILACCOUNT.NAME
});
WriteFile(#"C:\Temp\log.log",lQry.ToString());
}
SQL Development PC (everything is fine):
SELECT
"C"."ID_CONTACT" AS "ID_CONTACT",
"C"."READDATE" AS "READDATE",
"C"."DBID_CONTACT" AS "DBID_CONTACT",
"D"."DATEINSERT" AS "DATEINSERT",
"D"."FROMUSER" AS "FROMUSER",
"D"."FILENAME" AS "FILENAME",
"D"."KIND" AS "KIND",
"D"."MSGID" AS "MSGID",
"D"."ID_DBLINK" AS "ID_DBLINK",
"D"."DBID_DBLINK" AS "DBID_DBLINK",
"D"."SUBJECT" AS "SUBJECT",
"D"."STATE" AS "STATE",
"D"."DATEINCOME" AS "DATEINCOME",
"D"."DATESEND" AS "DATESEND",
"D"."ID_EMAILACCOUNT" AS "ID_EMAILACCOUNT",
"D"."DBID_EMAILACCOUNT" AS "DBID_EMAILACCOUNT",
"F"."GESCHST" AS "GESCHST",
"F"."KEYTYP" AS "KEYTYP",
"H"."NAME" AS "NAME"
FROM "CONTACTUSER" AS "C"
INNER JOIN "CONTACT" AS "D" ON ("C"."ID_CONTACT" = "D"."ID") AND ("C"."DBID_CONTACT" = "D"."DBID")
LEFT OUTER JOIN "DBLINK" AS "F" ON ("D"."ID_DBLINK" = "F"."ID") AND ("D"."DBID_DBLINK" = "F"."DBID")
INNER JOIN "EMAILACCOUNT" AS "H" ON ("D"."ID_EMAILACCOUNT" = "H"."ID") AND ("D"."DBID_EMAILACCOUNT" = "H"."DBID")
WHERE ((0 = "D"."ID_DBLINK") OR ("D"."ID_DBLINK" IS NULL)) OR ("F"."MANDNR" = #p__linq__0)
SQL Server (multible JOINS on DBLINK, very slow):
SELECT
"C"."ID_CONTACT" AS "ID_CONTACT",
"C"."READDATE" AS "READDATE",
"C"."DBID_CONTACT" AS "DBID_CONTACT",
"F"."DATEINSERT" AS "DATEINSERT",
"F"."FROMUSER" AS "FROMUSER",
"F"."FILENAME" AS "FILENAME",
"F"."KIND" AS "KIND",
"F"."MSGID" AS "MSGID",
"F"."ID_DBLINK" AS "ID_DBLINK",
"F"."DBID_DBLINK" AS "DBID_DBLINK",
"F"."SUBJECT" AS "SUBJECT",
"F"."STATE" AS "STATE",
"F"."DATEINCOME" AS "DATEINCOME",
"F"."DATESEND" AS "DATESEND",
"F"."ID_EMAILACCOUNT" AS "ID_EMAILACCOUNT",
"F"."DBID_EMAILACCOUNT" AS "DBID_EMAILACCOUNT",
"J"."GESCHST" AS "GESCHST",
"L"."KEYTYP" AS "KEYTYP",
"N"."NAME" AS "NAME"
FROM "CONTACTUSER" AS "C"
INNER JOIN "CONTACT" AS "D" ON ("C"."DBID_CONTACT" = "D"."DBID") AND ("C"."ID_CONTACT" = "D"."ID")
LEFT OUTER JOIN "CONTACT" AS "F" ON ("C"."DBID_CONTACT" = "F"."DBID") AND ("C"."ID_CONTACT" = "F"."ID")
LEFT OUTER JOIN "DBLINK" AS "H" ON ("F"."DBID_DBLINK" = "H"."DBID") AND ("F"."ID_DBLINK" = "H"."ID")
LEFT OUTER JOIN "DBLINK" AS "J" ON ("F"."DBID_DBLINK" = "J"."DBID") AND ("F"."ID_DBLINK" = "J"."ID")
LEFT OUTER JOIN "DBLINK" AS "L" ON ("F"."DBID_DBLINK" = "L"."DBID") AND ("F"."ID_DBLINK" = "L"."ID")
LEFT OUTER JOIN "EMAILACCOUNT" AS "N" ON ("F"."DBID_EMAILACCOUNT" = "N"."DBID") AND ("F"."ID_EMAILACCOUNT" = "N"."ID")
WHERE ((0 = "D"."ID_DBLINK") OR ("F"."ID_DBLINK" IS NULL)) OR ("H"."MANDNR" = #p__linq__0)
And here the code first data:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove();
modelBuilder.Entity<CONTACT>()
.HasOptional(b => b.DBLINK)
.WithMany()
.HasForeignKey(b => new { b.ID_DBLINK, b.DBID_DBLINK });
modelBuilder.Entity<CONTACT>()
.HasKey(d => new { d.ID, d.DBID })
.HasMany(d => d.CONTACTUSER)
.WithRequired(d => d.CONTACT)
.HasForeignKey(l => new { l.ID_CONTACT, l.DBID_CONTACT });
modelBuilder.Entity<CONTACT>()
.HasRequired(b => b.EMAILACCOUNT)
.WithMany()
.HasForeignKey(b => new { b.ID_EMAILACCOUNT, b.DBID_EMAILACCOUNT });
modelBuilder.Entity<CONTACT>().HasKey(a => new { a.ID, a.DBID });
modelBuilder.Entity<DBLINK>().HasKey(a => new { a.ID, a.DBID });
modelBuilder.Entity<CONTACTUSER>().HasKey(a => new { a.ID, a.DBID });
modelBuilder.Entity<EMAILACCOUNT>().HasKey(a => new { a.ID, a.DBID });
base.OnModelCreating(modelBuilder);
}
I'm searching for days now. How can the same code generate different SQL-Statements?
IIS Version (Development PC: IIS 8, Server IIS7.5)
Both environments are using the same Database-Server (Firebird 2.5)
I copied the whole project to the server, no changes...
The Projekt is set to .NET 4.5
I created a sample WPF-Projekt with this function and it resulted using the RIGHT SQL on the server
FirebirdSql.Data.FirebirdClient.dll Version 3.0.2.0 EntityFramework.dll 5.0.0
Do you have any idea what can infuence the query creation this way?
I'm getting an error with a query, my question is: can i chain joins?
My first join is to the primary table, but my second join is to the table joined to the primary table. This is the query:
$query = $this->getDbTable()->select()
->from(array('ca' => 'contracts_allotment'),
array('id',
'contracts_rooms_id' => new Zend_Db_Expr("CONCAT(room_type_desc, '-', room_characteristics_desc)")
))
->join(array('cr' => 'contracts_rooms'),
'ca.contract_rooms_id = cr.id',
array())
->join(array('rt' => 'room_types'),
'cr.room_id = rt.id',
array('room_type_desc'))
->join(array('rc' => 'room_characteristics'),
'cr.char_id = rc.id',
array('room_characteristics_desc'))
->where('contract_id = ?', $contractId);
var_dump($this->getDbTable()->fetchAll($query));die;
I'm getting:
Select query cannot join with another table"
The error comes from Zend/Db/Table/Select::assemble()
Here you have some inside assemble():
// Check each column to ensure it only references the primary table
if ($column) {
if (!isset($from[$table]) || $from[$table]['tableName'] != $primary) {
var_dump($from[$table]['tableName'], $primary);die;
require_once 'Zend/Db/Table/Select/Exception.php';
throw new Zend_Db_Table_Select_Exception('Select query cannot join with another table');
}
}
The var_dump() prints:
string(10) "room_types" string(19) "contracts_allotment"
Any idea?
Don't forget to lock the tables when doing joins:
$query = $this->getDbTable()->select()
->setIntegrityCheck(false)
->from(array('ca' => 'contracts_allotment'),
array('id',
'contracts_rooms_id' => new Zend_Db_Expr("CONCAT(room_type_desc, '-', room_characteristics_desc)")
))
->join(array('cr' => 'contracts_rooms'),
'ca.contract_rooms_id = cr.id',
array())
->join(array('rt' => 'room_types'),
'cr.room_id = rt.id',
array('room_type_desc'))
->join(array('rc' => 'room_characteristics'),
'cr.char_id = rc.id',
array('room_characteristics_desc'))
->where('contract_id = ?', $contractId);
->setIntegrityCheck(false) should at least get you a new error.