PostgreSQL: LEFT JOIN создает пустую строку - PullRequest
1 голос
/ 15 марта 2012

См. Важные новые открытия 1 и 2. В конце этого объяснения.

Я использую Postgres 9.1.3 и у меня странная проблема с левым соединением.

У меня есть таблица с именем последовательный. Мастер с более чем 2 миллионами строк.Он имеет столбец с именем citation_id , и этот столбец не имеет нулевых значений.Я могу убедиться в этом:

SELECT COUNT(*)
FROM consistent.master
WHERE citation_id IS NULL

Это возвращает 0 .

Вот где это становится странным: если я LEFT JOIN эта таблицаво временную таблицу я получаю сообщение об ошибке, что я пытаюсь вставить нулевое значение в поле citation_id :

ОШИБКА: нулевое значение в столбце "citation_id" нарушает ненулевое значениеСостояние SQL ограничения: 23502

Вот запрос:

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
)

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, id, source, citing_jurisdiction, the_geom)

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, id, source, citing_jurisdiction, the_geom
FROM consistent.master LEFT JOIN stops
ON stops.citation_id = master.citation_id

Я почесываю голову об этом.Если это LEFT JOIN , и если compatibility.master является левой таблицей объединения, как этот запрос может создать нулевые значения в столбце citation_id , если его нет?

Вот код SQL, который я использовал для создания таблицы:

CREATE TABLE consistent.masternew
(
  arrest_id character varying(20),
  citation_id character varying(20) NOT NULL,
  defendant_dl character varying(20),
  defendant_dl_state character varying(2),
  defendant_zip character varying(9),
  defendant_race character varying(10),
  defendant_sex character(1),
  defendant_dob date,
  vehicle_licenseplate character varying(10),
  vehicle_licenseplate_state character(2),
  vehicle_registration_expiration_date date,
  vehicle_year integer,
  vehicle_make character varying(20),
  vehicle_model character varying(20),
  vehicle_color character varying,
  offense_timestamp timestamp without time zone,
  offense_street_number character varying(10),
  offense_street_name character varying(30),
  offense_crossstreet_number character varying(10),
  offense_crossstreet_name character varying(30),
  offense_county character varying(10),
  officer_id character varying(20),
  offense_code integer,
  speed_alleged integer,
  speed_limit integer,
  work_zone bit(1),
  school_zone bit(1),
  offense_location point,
  id serial NOT NULL,
  source character varying(20), -- Where this citation came from--court, PD, etc.
  citing_jurisdiction integer,
  the_geom geometry,
  CONSTRAINT masternew_pkey PRIMARY KEY (id ),
  CONSTRAINT citing_jurisdiction FOREIGN KEY (citing_jurisdiction)
      REFERENCES consistent.jurisdictions (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT offenses FOREIGN KEY (offense_code)
      REFERENCES consistent.offenses (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT enforce_dims_the_geom CHECK (st_ndims(the_geom) = 2),
  CONSTRAINT enforce_geotype_the_geom CHECK (geometrytype(the_geom) = 'POINT'::text OR the_geom IS NULL),
  CONSTRAINT enforce_srid_the_geom CHECK (st_srid(the_geom) = 3081)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE consistent.masternew
  OWNER TO postgres;
COMMENT ON COLUMN consistent.masternew.source IS 'Where this citation came from--court, PD, etc.';

CREATE INDEX masternew_citation_id_idx
  ON consistent.masternew
  USING btree
  (citation_id COLLATE pg_catalog."default" );

CREATE INDEX masternew_citing_jurisdiction_idx
  ON consistent.masternew
  USING btree
  (citing_jurisdiction );

CREATE INDEX masternew_defendant_dl_idx
  ON consistent.masternew
  USING btree
  (defendant_dl COLLATE pg_catalog."default" );

CREATE INDEX masternew_id_idx
  ON consistent.masternew
  USING btree
  (id );

CREATE INDEX masternew_offense_street_name_idx
  ON consistent.masternew
  USING btree
  (offense_street_name COLLATE pg_catalog."default" );

CREATE INDEX masternew_offense_street_number_idx
  ON consistent.masternew
  USING btree
  (offense_street_number COLLATE pg_catalog."default" );

CREATE INDEX masternew_offense_timestamp_idx
  ON consistent.masternew
  USING btree
  (offense_timestamp );

CREATE INDEX masternew_the_geom_idx
  ON consistent.masternew
  USING gist
  (the_geom );

ВАЖНОЕ ОТКРЫТИЕ 1

Я простообнаружил что-то интересное.Этот запрос:

SELECT COUNT(*)
FROM consistent.master
WHERE citation_id IS NOT NULL
UNION
SELECT COUNT(*)
FROM consistent.master
UNION
SELECT COUNT(*)
FROM consistent.master
WHERE citation_id IS NULL

Результаты:

2085344
2085343
0

Как я могу это объяснить?Как может счет с WHERE citation_id IS NOT NULL возможно быть выше, чем тот же запрос без предложения WHERE?

ВАЖНОЕ ОТКРЫТИЕ 2 ОК, согласно комментариям ниже, я обнаружил, что у меня естьстрока со всеми пустыми значениями, и это несмотря на тот факт, что в таблице есть последовательный столбец id и некоторые ограничения NOT NULL.

Я удалил строку задницы.Теперь я не получаю нулевую ошибку.Вместо этого я получаю это:

ERROR:  duplicate key value violates unique constraint "masternew_pkey"
DETAIL:  Key (id)=(1583804) already exists.

********** Error **********

ERROR: duplicate key value violates unique constraint "masternew_pkey"
SQL state: 23505
Detail: Key (id)=(1583804) already exists.

Итак, просто чтобы убедиться, что я делаю этот запрос:

SELECT COUNT(id)
FROM consistent.master
WHERE id=1583804;

Угадайте что?consistent.master имеет только 1 экземпляр этого!Таким образом, учитывая, что левая таблица в LEFT JOIN только 1 экземпляре 1583804 в citation_id и , столбец id может исходить только из левой таблицыКак эта ошибка может произойти?Подобное LEFT JOIN не должно приводить к тому, что в конечном результате будет больше строк, чем в левой таблице, верно?

Ответы [ 2 ]

3 голосов
/ 15 марта 2012

С INSERT, особенно со сложным, вы должны всегда определять целевые столбцы .Так что сделайте это:

INSERT INTO consistent.masternew <b>(citation_id, col1, col2, ...)</b>

Если что-то пойдет не так в прилагаемом операторе SELECT, например:

the_geom geometry

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

PostgreSQL не применяет такое же количество столбцов в операторе SELECT, как в целевой таблице,Я цитирую прекрасное руководство по этому вопросу :

Каждый столбец, отсутствующий в списке явных или неявных столбцов, будет заполнен значением по умолчанию, либо его объявленным значением по умолчанию или ноль, если его нет .

(выделение жирным шрифтом.) Если в списке столбцов имеется несоответствие, это может привести к появлению значения NULL «из ниоткуда».

Кроме того, порядок столбцов в операторе SELECT должен соответствовать порядку столбцов, в которые необходимо вставить.Если целевые столбцы не прописаны, это будет порядок столбцов в вашей таблице в том виде, в котором она была создана.
Похоже, вы ожидаете, что столбцы сопоставляются по имени автоматически, но это не так.Имена столбцов в операторе SELECT не имеют никакого отношения к последнему шагу INSERT.Значителен только их порядок слева направо.

Вопреки тому, что другие подразумевали, предложение WITH совершенно законно .Я цитирую руководство по INSERT :

Возможно, что запрос (оператор SELECT) также может содержать предложение WITH.В таком случае на оба набора with_query можно ссылаться в запросе, но второй имеет приоритет, поскольку он более тесно вложен.

Ваше утверждение может выглядеть следующим образом:

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
    )
INSERT INTO consistent.masternew (citation_id, col1, col2, ...) -- add columns
SELECT m.citation_id -- order colums accordingly!
      ,s.stop
      ,m.defendant_dl
        -- 27 more columns
      ,m.citing_jurisdiction
      ,m.the_geom
FROM   consistent.master m
LEFT   JOIN stops s USING (citation_id);
2 голосов
/ 15 марта 2012

В предположении я бы сказал, что вы вставляете stop.stop, который может быть нулевым, в столбец citation_id, но не зная структуру таблицы, я не могу сказать точно:)

РЕДАКТИРОВАТЬ: попробуйте предложение @ vol7ron и назовите столбцы ...

...