У меня есть база данных с типы и категории (среди прочего, несвязанных вещей). Типы имеет отношение "многие к одному" с категории. Я хочу выбрать все строки типов, упорядоченные сначала по имени категории, а затем по весу типа и, наконец, по имени типа (все по возрастанию). Критическая часть заключается в том, что я хочу, чтобы все типы с одной и той же категорией были сгруппированы в результате. Будучи новичком в SQL, я думал, что простого оператора соединения, за которым следует соответствующий порядок следования операторов, будет достаточно. Я был неправ. Результат, который я получаю, не имеет для меня смысла.
Фактический результат (имя типа - название категории):
Ожидаемый результат (имя типа - название категории):
Существует более 2000 строк типов, поэтому приведенные выше списки, очевидно, очень усечены. Ошибок нет. Вероятно, также важно, чтобы результаты были разбиты на страницы (что отлично работает).
Я использую Doctrine 2.5.x с собственной системой управления контентом. В репозитории для сущности типа я использую QueryBuilder для построения запроса следующим образом (максимальный результат является глобальной настройкой, а первый результат вычисляется вне репозитория на основе текущего номера страницы):
$qb = $this->createQueryBuilder('t');
$qb->select('t, c'); // omitting this does not change the result
$qb->join('t.category', 'c');
$qb->addOrderBy('c.name', 'ASC'); // this does not work as expected
$qb->addOrderBy('t.weight', 'ASC');
$qb->addOrderBy('t.name', 'ASC');
$qb->setMaxResults(20);
$qb->setFirstResult(0);
return $qb->getQuery()->getResult();
Результирующий оператор SQL выглядит следующим образом:
SELECT b0_.ID AS ID_0, b0_.weight AS weight_1, b0_.name AS name_2, b0_.number AS number_3, b0_.name_alt AS name_alt_4, b0_.note AS note_5, b1_.ID AS ID_12, b1_.slug AS slug_13, b1_.name AS name_14, b1_.`desc` AS desc_15, b0_.cat_id AS cat_id_16 FROM types b0_ INNER JOIN categories b1_ ON b0_.cat_id = b1_.ID ORDER BY b1_.name ASC, b0_.weight ASC, b0_.name ASC LIMIT 20 OFFSET 0
Сущность типа настроена следующим образом (с опущенными нерелевантными полями):
/**
* @Entity(repositoryClass = "Nevermind\Repository\TypeRepository")
* @Table(name = "types", options = {"collate" = "utf8mb4_unicode_ci", "charset" = "utf8mb4"})
*/
class Type {
/**
* @Id
* @Column(type = "integer", name = "ID")
* @GeneratedValue
*/
protected $id;
/**
* @ManyToOne(targetEntity = "Category", inversedBy = "types", fetch = "EAGER")
* @JoinColumn(name = "cat_id", referencedColumnName = "ID")
*/
protected $category;
/**
* @Column(type = "integer")
*/
protected $weight = 0;
/**
* @Column(type = "string", unique=true)
*/
protected $name = '';
/**
* @Column(type = "string", unique=true, length=12)
*/
protected $number = '';
/**
* @Column(type = "string", nullable=true)
*/
protected $name_alt;
/**
* @Column(type = "text", nullable=true)
*/
protected $note;
}
И объект категории настроен следующим образом:
/**
* @Entity(repositoryClass = "Nevermind\Repository\DefaultRepository")
* @Table(name = "categories", options = {"collate" = "utf8mb4_unicode_ci", "charset" = "utf8mb4"})
*/
class Category {
/**
* @Id
* @Column(type = "integer", name = "ID")
* @GeneratedValue
*/
protected $id;
/**
* @Column(type = "string", unique=true)
*/
protected $slug = '';
/**
* @Column(type = "string", nullable=true)
*/
protected $name;
/**
* @Column(type = "text", name = "`desc`", nullable=true)
*/
protected $desc;
/**
* @OneToMany(targetEntity = "Type", mappedBy = "category", fetch = "EXTRA_LAZY", cascade = {"persist", "remove"})
* @OrderBy({"weight" = "ASC", "name" = "ASC"})
*/
protected $types;
}
TypeRepository расширяет DefaultRepository, который расширяет EntityRepository Doctrine.
Готово, хотя мне пришлось использовать $qb->getQuery()->getSQL(), так как $qb->getSqlQuery() выдает несуществующую ошибку метода.
Я помещаю SQL непосредственно в phpMyAdmin, и он выглядит точно так же, как и в HTML для моей CMS (в том числе для последующих страниц, если я изменю номер OFFSET)
Пожалуйста, отредактируйте вопрос, чтобы он включал первые несколько строк вывода оператора SQL при запуске в phpMyAdmin, т. е. включал все столбцы, а также включал первые несколько строк таблиц типов и категорий. Спасибо.
Простите меня, но мне кажется, что я задаю слишком много вопросов (тем более, что мой вопрос и без того очень длинный), поэтому не могли бы вы помочь мне понять, что можно получить, имея всю эту постороннюю информацию? Я надеялся, что это будет относительно распространенный вид оператора select, который должен знать типичный опытный разработчик SQL, а не поиск ошибок.
Я провел лучший эксперимент, который мог, используя ваш оператор sql и несколько строк данных, и сортировка вышла правильной. Поэтому я задался вопросом, были ли данные каким-то образом проблемой, и поэтому хотел посмотреть на ваши фактические строки. Думаю, подойдет одна строка каждой таблицы. Это вовсе не обычная проблема с SQL (как вы можете судить по количеству голосов по этому вопросу).
Можете попробовать $q = $qb->getQuery(); $q->setHint(Query::HINT_REFRESH, true);? Вы запрашиваете какие-либо типы перед этим кодом построителя запросов?
@MandyShaw Вы были правы - это были фактические данные. Я совершенно забыл, что я изменил имя категории (при отображении), чтобы оно было получено либо из столбца имени, либо (если столбец имени равен NULL) преобразовано из столбца slug. Таким образом, изменение запроса для использования category.slug устраняет проблему., потому что столбец slug никогда не равен NULL. Спасибо, что указали мне правильное направление!
Пожалуйста, поместите решение в ответ и примите его, чтобы будущим исследователям было ясно, что произошло (комментарии могут исчезнуть со временем, а также этот вопрос все еще будет появляться как оставшийся без ответа - добавление «решено» в заголовок не является обычной практикой на ТАК). Спасибо.
Ой, извини. Я думаю, вы можете сказать, что я здесь новенький :/






Ты пробовал
$qb->select('t, c');
...
$qb->orderBy('c.name', 'ASC'); // orderBy not addOrderBy for the first clause
$qb->addOrderBy('t.weight', 'ASC');
$qb->addOrderBy('t.name', 'ASC');
...
У меня - не менял вывод.
Странный порядок результатов был вызван тем, что столбец категории name иногда имеет вид null (при отображении имя возвращается к преобразованному содержимому столбца slug, изменение, о котором я совершенно забыл). Поэтому простое изменение первого оператора порядка на c.slug вместо этого решает проблему. О!
Так что помните, упорядочение по столбцам, которые могут быть нулевыми, приведет к странным результатам!
Для отладки я бы
echo $qb->getSqlQuery();увидел, что фактический оператор SQL отправляется на сервер MySQL, и протестировал его. Это помогло бы нам определить причину неожиданного поведения... это проблема в тексте SQL-запроса или проблема в обработке возврата.