Postgres LEFT JOIN создает больше строк, чем в левой таблице - PullRequest
1 голос
/ 19 марта 2012

Я использую 32-разрядную версию Postgres 9.1.3 в Windows 7 x64. (Необходимо использовать 32-разрядную версию, поскольку нет версии Windows PostGIS, совместимой с 64-разрядной версией Postgres.) ( EDIT : Начиная с PostGIS 2.0 она совместима с 64-разрядной версией Postgres для Windows.)

У меня запрос, который left объединяет таблицу (consistent.master) с временной таблицей, а затем вставляет полученные данные в третью таблицу (consistent.masternew).

Поскольку это left join, результирующая таблица должна иметь то же количество строк, что и левая таблица в запросе. Однако, если я запускаю это:

SELECT count(*)
FROM consistent.master

Я получаю 2085343. Но если я запущу это:

SELECT count(*)
FROM consistent.masternew

Я получаю 2085703.

Как masternew может иметь больше строк, чем master? Разве masternew не должно иметь такое же количество строк, как master, левая таблица в запросе?

Ниже приведен запрос. Таблицы master и masternew должны иметь одинаковую структуру.

--temporary table created here
--I am trying to locate where multiple tickets were written on
--a single traffic stop
WITH stops AS (
    SELECT citation_id,
           rank() OVER (ORDER BY offense_timestamp,
                     defendant_dl,
                     offense_street_number,
                     offense_street_name) AS stop
    FROM   consistent.master
    WHERE  citing_jurisdiction=1
)

--Here's the insert statement. Below you'll see it's
--pulling data from a select query
INSERT INTO consistent.masternew (arrest_id,
  citation_id,
  defendant_dl,
  defendant_dl_state,
  defendant_zip,
  defendant_race,
  defendant_sex,
  defendant_dob,
  vehicle_licenseplate,
  vehicle_licenseplate_state,
  vehicle_registration_expiration_date,
  vehicle_year,
  vehicle_make,
  vehicle_model,
  vehicle_color,
  offense_timestamp,
  offense_street_number,
  offense_street_name,
  offense_crossstreet_number,
  offense_crossstreet_name,
  offense_county,
  officer_id,
  offense_code,
  speed_alleged,
  speed_limit,
  work_zone,
  school_zone,
  offense_location,
  source,
  citing_jurisdiction,
  the_geom)

--Here's the select query that the insert statement is using.    
SELECT stops.stop,
  master.citation_id,
  defendant_dl,
  defendant_dl_state,
  defendant_zip,
  defendant_race,
  defendant_sex,
  defendant_dob,
  vehicle_licenseplate,
  vehicle_licenseplate_state,
  vehicle_registration_expiration_date,
  vehicle_year,
  vehicle_make,
  vehicle_model,
  vehicle_color,
  offense_timestamp,
  offense_street_number,
  offense_street_name,
  offense_crossstreet_number,
  offense_crossstreet_name,
  offense_county,
  officer_id,
  offense_code,
  speed_alleged,
  speed_limit,
  work_zone,
  school_zone,
  offense_location,
  source,
  citing_jurisdiction,
  the_geom
FROM consistent.master LEFT JOIN stops
ON stops.citation_id = master.citation_id

В случае, если это имеет значение, я запустил VACUUM FULL ANALYZE и переиндексировал обе таблицы. (Не уверен в точных командах; сделал это через pgAdmin III.)

Ответы [ 2 ]

9 голосов
/ 19 марта 2012

В левом соединении необязательно должно быть столько же строк, сколько в левой таблице. По сути, это похоже на обычное объединение, за исключением того, что добавляются строки левой таблицы, которые не отображаются в обычном объединении. Таким образом, если в правой таблице более одной строки, совпадающей с одной строкой в ​​левой таблице, в ваших результатах может быть больше строк, чем количество строк в левой таблице.

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

select citation_id
from stops join master on stops.citation_id = master.citation_id
group by citation_id
having count(*) > 1
0 голосов
/ 28 июля 2018

Иногда вы знаете, что есть несколько магазинов, но все равно. Вы просто хотите взять первую или верхнюю запись.
Если это так, вы можете использовать SELECT DISTINCT ON:

FROM consistent.master LEFT JOIN (SELECT DISTINCT ON (citation_id) * FROM stops) s
ON s.citation_id = master.citation_id

Где citation_id - это столбец, в котором вы хотите взять первую (любую) строку для каждого совпадения.

Возможно, вы захотите убедиться, что это является детерминированным, и используйте ORDER BY с некоторым другим столбцом, который можно заказать:

SELECT DISTINCT ON (citation_id) * FROM stops ORDER BY citation_id, created_at
...