2011-05-27 64 views
3

我在PHP5.3的Symfony 1.4中使用Doctrine 1.2。我有一個數據庫,其中事件有多個開始日期時間(1:n)和類型(n:m)。學說在使用連接和偏移時跳過數據集

對於一個計劃,我希望的開始時間和事件的標題,這屬於一種特殊類型,而不是過時的列表:

$dql = Doctrine_Query::create() 
    ->select('ed.start_datetime,e.title')/*edd.*,*/ 
    ->from('EventDate ed') 
    ->leftJoin('ed.Event e') 
    ->leftJoin('e.EventTypes et') 
    ->leftJoin('e.Domain d') 
    ->orderBy('ed.start_datetime, ed.event_id'); 
    ->where('e.start_datetime >= ? AND e3.id = ?',array('2011-05-27 17:19:41',2)) 
    ->fetchArray(); 

該模式可在pastebin.com/SdjvsaxW進行審查。

現在,學說使得兩個語句

SELECT DISTINCT e5.event_id FROM event_date e5 LEFT JOIN event e6 ON e5.event_id = e6.id LEFT JOIN event_event_type e8 ON (e6.id = e8.event_id) LEFT JOIN event_type e7 ON e7.id = e8.event_type_id WHERE (e5.start_datetime >= '2011-05-27 17:19:41' AND e7.id = '2') ORDER BY e5.start_datetime, e5.event_id 

SELECT e.event_id AS e__event_id, e.start_datetime AS e__start_datetime, e2.id AS e2__id, e2.title AS e2__title FROM event_date e LEFT JOIN event e2 ON e.event_id = e2.id LEFT JOIN event_event_type e4 ON (e2.id = e4.event_id) LEFT JOIN event_type e3 ON e3.id = e4.event_type_id WHERE e.event_id IN (*all the IDs from the first query*) AND (e.start_datetime >= '2011-05-27 17:19:41' AND e3.id = '2') ORDER BY e.start_datetime, e.event_id 

,當我介紹分頁的問題開始:當我加一個偏移量,它會在第一個查詢中添加。現在,由於可能有多個事件的開始時間,因此第一個SELECT DISTINCT查詢的ID數量可能會小於日期數量。因此,日期可能會被跳過。就我而言,甚至有不到50個不同的事件,所以第二頁顯示事件日期51-100確實是空的,儘管事實上有幾百個事件日期(由於重複發生的事件)。

我認爲這個問題可能會被解決,但我不能控制這個問題。

任何任何想法?

+0

您的架構,請。此外,你試圖分頁的結果是什麼(例如EventDate或Event)? – Dziamid 2011-05-27 21:20:51

+0

您是否在使用sfDoctrinePager進行分頁? – Pabloks 2011-05-27 23:55:27

+0

請注意,您錯過了「?」從您的DQL中的WHERE子句開始。您可以通過在Doctrine原始SQL中寫入來擺脫DISTINCT。 – Tom 2011-05-28 00:26:14

回答

3

當您擁有基表並通過一對多關係進行連接時,Doctrine會在稱爲「限制子查詢」的模式下運行兩個查詢,第一個是不同的查詢。這是因爲您要選擇連接的產品,並且最終將獲得比基表中的記錄更多的行。例如,如果Event#5有兩個EventType,數據庫將在兩行中返回事件#5。這兩個查詢行爲僅在MySQL中,其他數據庫可以在子查詢中運行限制,Doctrine生成單個查詢。

由於您的事件有多個日期,並且EventDate有一個複合主鍵,所以Doctrine應該在第一個查詢中選擇一組不同的(event_id,start_datetime),但它似乎忽略了複合主鍵的第二部分,可能是因爲它不能將它們填入第二個查詢中的ON子句中。

你也許可以用兩種或三種方法之一解決這個問題: 1)爲EventDate(autoincrement列)添加一個唯一的主鍵。你可能不需要在其他地方引用它,但是學說將開始使用它。2)手動執行2個查詢。選擇所有不同的事項標識,start_datetime從EVENTDATE,加入你的極限和偏移量,然後以編程方式創建一個WHERE子句:

... WHERE ((event_id = ? AND start_datetime = ?) OR (event_id = ? AND start_datetime = ?))... 

3)重新排序的查詢,從不同的表開始或添加DISTINCT的選擇。嘗試選擇不同的(ed.event_id,ed.start_datetime)。有時候這些東西可以避免限制子查詢算法被觸發。 4)關閉這個系統,看下面的鏈接,從來沒有嘗試過這一點,我不知道水合作用和教條對象緩存有什麼副作用。

就個人而言,我會去#1。我發現使用Doctrine獲得額外的主鍵列總是很有幫助的,即使您可以在多個列上形成主鍵。

如果您想查看代碼原則用於此目的,請參閱lib/Doctrine/Record.php,函數getLimitSubquery()。你會注意到註釋「//關於複合鍵怎麼樣?」在代碼中兩次。您也可以閱讀http://www.doctrine-project.org/documentation/manual/1_2/en/dql-doctrine-query-language:limit-and-offset-clauses:the-limit-subquery-algorithm