在Doctrine 2中选择日期之间的条目

68

我因为这个小错误而疯狂,无法解决。我想选择两天之间的条目,以下示例说明了我的所有失败:

选择1.

$qb->where('e.fecha > ' . $monday->format('Y-m-d'));
$qb->andWhere('e.fecha < ' . $sunday->format('Y-m-d'));

结果(0个条目):

SELECT r0_.id_reservacion AS id_reservacion0, r0_.fecha AS fecha1, r0_.cliente AS cliente2 
FROM reservacion r0_ 
WHERE (r0_.fecha > 2012 - 07 - 16) AND (r0_.fecha < 2012 - 07 - 22)

优化方案2

$qb->add('where', 'e.fecha between 2012-01-01 and 2012-10-10');

结果(0个条目):

SELECT r0_.id_reservacion AS id_reservacion0, r0_.fecha AS fecha1, r0_.cliente AS cliente2 
FROM reservacion r0_ WHERE r0_.fecha 
BETWEEN 2012 - 01 - 01 AND 2012 - 10 - 10

这是我的表格,其中包含当前条目:

id      fecha            cliente
1   2012-07-16 00:00:00    2    
2   2012-07-16 13:00:00    4    
3   2012-07-22 23:00:00    4

编辑 1

为了验证 SQL 以消除疑虑,我运行了这个查询:

$qb->where('e.fecha > ' . $sunday->format('Y-m-d'));

结果(共3项):

SELECT r0_.id_reservacion AS id_reservacion0, r0_.fecha AS fecha1, r0_.cliente AS cliente2 

看起来 SQL 不是问题所在。 FROM reservacion r0_ WHERE r0_.fecha > 2012 - 07


在专家到来之前,只是一个问题 - 你需要将这些日期转换为字符串吗?我的意思是,例如第一个生成的WHERE语句中有r0_.fecha > 2012-07-16 - 它不应该更像r0_.fecha > '2012-07-16'或类似的形式吗? - azhrei
完全同意你的观点。但请看更新后的帖子。 - manix
嗯,好的 - 如果你引用的话会发生什么? - azhrei
嗯,我肯定在疯狂的时刻犯了一个错误或打字错误。引用日期不会影响查询:D(就像我一开始想的那样)。 - manix
4个回答

175

你可以选择...

$qb->where('e.fecha BETWEEN :monday AND :sunday')
   ->setParameter('monday', $monday->format('Y-m-d'))
   ->setParameter('sunday', $sunday->format('Y-m-d'));

或者...

$qb->where('e.fecha > :monday')
   ->andWhere('e.fecha < :sunday')
   ->setParameter('monday', $monday->format('Y-m-d'))
   ->setParameter('sunday', $sunday->format('Y-m-d'));

5
+2 表示参数数量。如果不使用参数,则很危险,因为Doctrine无法正确转义它们。 - Harry Mustoe-Playfair
19
这可能会破坏与某些DB平台的兼容性,比如Microsoft SQL Server,那里没有Date类型,所有日期都以"Y-m-d H:i:s.000"的形式保存。你应该通过在setParameter()的第三个参数中定义类型,像这样setParameter('monday', $monday, \Doctrine\DBAL\Types\Type::DATE),让Doctrine处理转换。 - flu
1
模拟“BETWEEN”的比较运算符是“> =”和“<=”,而不是“>”和“<”。 - Courtney Miles
请注意,虽然 BETWEEN>=/<= 在逻辑上是等价的,但在 MySQL 中使用 BETWEEN 可能会使您的查询性能更好 -- 它可以鼓励查询规划器更好地选择如何使用相关索引。 - Courtney Miles
1
你也可以使用:->where($qb->exp()->between('e.fecha', ':monday', ':sunday') - Matías Navarro Carter
1
@MatíasNavarroCarter:->where($qb->expr()->between('e.fecha', ':monday', ':sunday')你漏掉了expr()中的r - BendaThierry.com

48

我认为正确的做法是使用查询构建器表达式:

$now = new DateTimeImmutable();
$thirtyDaysAgo = $now->sub(new \DateInterval("P30D"));
$qb->select('e')
   ->from('Entity','e')
   ->add('where', $qb->expr()->between(
            'e.datefield',
            ':from',
            ':to'
        )
    )
   ->setParameters(array('from' => $thirtyDaysAgo, 'to' => $now));

http://docs.doctrine-project.org/en/latest/reference/query-builder.html#the-expr-class

编辑:这种方法的优点是独立于任何其他答案中的数据库软件 - 你应该让Doctrine处理日期类型,因为它有一个抽象层来处理这种情况。

如果您像添加一个字符串变量的形式'Y-m-d',那么当它去到除MySQL之外的另一个数据库平台时,它将会出现问题,例如。

--- 另一个例子:

此示例使用大于和小于的方法创建了一个between条件。

if ($updateDateTime instanceof DateTime) {
    $qb->andWhere(
        $qb->expr()->gte('c.updated', ':updateDateTimeStart'),
        $qb->expr()->lt('c.updated', ':updateDateTimeEnd'),
    );

    $updateDateTimeImmutable = DateTimeImmutable::createFromMutable($updateDateTime);
    $start = $updateDateTimeImmutable->setTime(0,0,0, 0);
    $end = $start->modify('+1 day');

    $qb->setParameter('updateDateTimeStart', $start, Types::DATE_IMMUTABLE);
    $qb->setParameter('updateDateTimeEnd', $end, Types::DATE_IMMUTABLE);
}

非常感谢您的回答...这是唯一可以进行日期比较的方法...其他答案中提供的方法都对我无效 :) - Vidhi
谢谢 - 这确实是在Doctrine中进行日期比较的唯一真正平台无关的方法。 - Harry Mustoe-Playfair
我同意,但应该是->setParameters(array(':from' => $thirtyDaysAgo, ...,对吗? - Andresch Serj
1
@AndreschSerj:实际上不应该这样。请参阅http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/query-builder.html#binding-parameters-to-your-query。 - helvete

3

编辑:查看其他答案获取更好的解决方案。

我提供的原始新手方法是(opt1):

$qb->where("e.fecha > '" . $monday->format('Y-m-d') . "'");
$qb->andWhere("e.fecha < '" . $sunday->format('Y-m-d') . "'");

并且(选项2):
$qb->add('where', "e.fecha between '2012-01-01' and '2012-10-10'");

这非常迅速简便,并且能立即让原始帖子继续进行。

因此,这被接受为答案。

根据评论,这不是正确答案,但这是一个容易犯的错误,因此我将其保留在这里作为“不应该做的事情”的例子!


我选择了你的第二个选项,它完美地工作了。这个愚蠢的查询占用了我整个下午的时间,哇!太好了,非常感谢你! - manix
3
这种做法其实非常错误,你不应该直接将变量连接到查询中 - 你真的应该使用参数代替:http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/security.html - Harry Mustoe-Playfair
虽然它确实可以工作,但这是错误的甚至危险的,最好完全删除答案... - NDM

1

看看我如何在参数中格式化日期$jour。 这取决于您是使用expr()->like还是expr()->lte。

$qb
        ->select('e')
        ->from('LdbPlanningBundle:EventEntity', 'e')
        ->where(
            $qb->expr()->andX(
                $qb->expr()->orX(
                    $qb->expr()->like('e.start', ':jour1'),
                    $qb->expr()->like('e.end', ':jour1'),
                    $qb->expr()->andX(
                        $qb->expr()->lte('e.start', ':jour2'),
                        $qb->expr()->gte('e.end', ':jour2')
                    )
                ),
                $qb->expr()->eq('e.user', ':user')
            )
        )
        ->andWhere('e.user = :user ')
        ->setParameter('user', $user)
        ->setParameter('jour1', '%'.$jour->format('Y-m-d').'%')
        ->setParameter('jour2', $jour->format('Y-m-d'))
        ->getQuery()
        ->getArrayResult()
    ;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接