Неоднозначность в запросе SQL - PullRequest
0 голосов
/ 01 июля 2018

Существует две таблицы: таблица customer состоит из информации о клиентах, а таблица payment состоит из информации о платежах. Первичный ключ customer_id в таблице customer является внешним ключом в таблице payment_id. Следующие два запроса возвращают идентичные результаты:

SELECT
  payment.customer_id,
  last name,
  amount
FROM customer
INNER JOIN payment ON customer.customer_id = payment.customer_id

SELECT
  customer.customer_id,
  last_name,
  amount
FROM customer
INNER JOIN payment ON customer.customer_id = payment.customer_id

Единственное различие между запросами заключается в первом аргументе в предложении SELECT: payment.customer_id против customer.customer_id. Поскольку customer_id является столбцом, к которому объединяются таблицы, различие между payment.customer_id и customer.customer_id кажется бессмысленным. Однако, если я попытаюсь опустить таблицу в запросе:

SELECT
  customer_id,
  last_name,
  amount
FROM customer
INNER JOIN payment ON customer.customer_id = payment.customer_id

Я получаю

[42702] ОШИБКА: ссылка на столбец "customer_id" неоднозначна

Не могли бы вы описать, где неопределенность в запросе?

Ответы [ 6 ]

0 голосов
/ 02 июля 2018

Устаревшие объединения, такие как INNER JOIN, создают повторяющиеся столбцы. Использование INNER JOIN в вашем запросе создает два столбца с именем customer_id. В языке SQL есть обходной путь: вы должны поставить перед столбцом переменную диапазона, как и другие здесь предлагают (хотя и используют вводящий в заблуждение термин «псевдоним таблицы»).

К счастью, язык SQL также имеет исправление для этой проблемы: NATURAL JOIN не создает дублирующихся столбцов, поэтому вам не нужно устранять их неоднозначность:

SELECT
  customer_id,
  last_name,
  amount
FROM customer
NATURAL JOIN payment

Объединения, которые создают повторяющиеся столбцы, остаются, потому что из языка SQL ничего не удаляется («оковы совместимости»). Но вам не нужно ничего, кроме NATURAL JOIN.

Идея состоит в том, что имена ваших элементов данных означают одно и то же во всем словаре данных, например amount означает одну вещь (относящуюся к платежам) и только одну вещь (не существует amount, которая относится к клиентам или любому другому типу).

Иногда вам может понадобиться «спроецировать» столбцы, в которых вы не хотите участвовать NATURAL JOIN например.

WITH
C AS ( SELECT customer_id, last_name FROM customer ),
P AS ( SELECT customer_id, amount FROM payment )
SELECT
  customer_id,
  last_name,
  amount
FROM C 
NATURAL JOIN P

Это также «защищает» ваш код, например в маловероятном случае, если кто-либо добавит атрибут last_name к платежам.

0 голосов
/ 01 июля 2018

То, что с помощью теста на равенство совпадают два столбца, не означает, что они имеют одинаковое значение.

Два столбца могут быть разных типов, например, целое число и число с плавающей запятой или числовой и т. Д.

Или это может быть citext , что делает сравнения без учета регистра (одна таблица может иметь 'RedRum', а другая 'redruM').

часто условие соединения может не соответствовать строгому равенству (например, сравнение диапазона сети или совпадение префикса)

Во всех этих случаях, какая таблица используется для столбца результата, имеет значение.

если вы выполняете внешнее соединение, имя таблицы снова имеет значение.

Postgresql не знает, когда = означает, что таблица может подразумеваться, а когда нет, требует ее всегда.

Основное правило: при объединении таблиц указывайте таблицу каждого столбца, который вы используете в запросе. таким образом, все не сломается, если кто-то добавит столбцы в другую таблицу.

0 голосов
/ 01 июля 2018

Не могли бы вы описать, где неопределенность в запросе?

Логически в запросе нет двусмысленности, поскольку оба столбца должны иметь одинаковые значения. Однако может возникнуть неоднозначность, когда вы используете LEFT JOIN вместо INNER JOIN, например ::

INSERT INTO customer (customer_id, last_name) VALUES
(1, 'Smith'),
(2, 'Jones');

INSERT INTO payment (customer_id, amount) VALUES
(1, 100);

SELECT
    customer.customer_id,
    payment.customer_id,
    last_name,
    amount
FROM customer
LEFT JOIN payment ON customer.customer_id = payment.customer_id

 customer_id | customer_id | last_name | amount 
-------------+-------------+-----------+--------
           1 |           1 | Smith     |    100
           2 |             | Jones     |       
(2 rows)

Анализатор просто следует общим правилам и не анализирует запрос, чтобы выяснить, когда может появиться потенциальная неоднозначность.

0 голосов
/ 01 июля 2018

Хорошей практикой всегда является добавление к столбцу псевдонима таблицы / подзапроса.

Но в вашем случае (в обеих таблицах используются только имена PK / FK) вы также можете использовать предложение USING:

SELECT
  customer_id,
  last_name,
  amount
FROM customer
JOIN payment USING(customer_id);

Демоверсия DBFiddle


Существует и третье возможное решение, но я настоятельно рекомендую его не использовать:

SELECT
  customer_id,
  last_name,
  amount
FROM customer
NATURAL JOIN payment
0 голосов
/ 01 июля 2018

Ошибка означает, что два столбца имеют одинаковое имя customer_id, пусть механизм БД не знает, какой столбец вы хотите запросить.

Вам необходимо явно указать механизму БД имя столбца, который вы хотите запросить.

К таблицам может быть добавлен новый столбец после создания таблицы, новый столбец может совпадать со старым именем столбца, если вы не указали явно SELECT столбцы таблицы запроса в выборе, который будет ошибка в исходном запросе.

Вот несколько советов для вас

  • Вы можете присвоить таблице запросов псевдоним, а ваш запрос уточнит.

  • Четко укажите SELECT столбцы таблицы запроса при выборе из имени таблицы из-за таблиц

Если last_name столбец в payment таблице и amount столбец в customer

Вы можете сделать это.

SELECT
  c.customer_id,
  p.last_name,
  c.amount
FROM customer c
INNER JOIN payment p ON c.customer_id = p.customer_id
0 голосов
/ 01 июля 2018

Вы ответили на свой вопрос, опустив таблицу в утверждении выбора. Не указав его, SQL не знает, к какой таблице относится customer_id.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...