Вопрос или проблема
-- скрипт создания таблицы
CREATE TABLE employees
(
EMPLOYEE_ID NUMBER(6) NOT NULL,
FIRST_NAME VARCHAR2(20),
LAST_NAME VARCHAR2(25) NOT NULL,
SALARY NUMBER(8,2),
HIREDATE DATE,
DEPARTMENT_ID NUMBER(4)
);
CREATE SEQUENCE emp_seq --Создание последовательности
START WITH 1
INCREMENT BY 1
NOCACHE;
Вопрос: когда я попытался выполнить этот запрос на вставку, я получил ошибку “недостаточно значений”:
INSERT INTO employees (employee_id, last_name, hiredate)
VALUES ((SELECT emp_seq.nextval, 'Smith', SYSDATE FROM dual));
Может кто-то объяснить, почему?
Я пытался выполнять вложенный запрос по отдельности, получая желаемый результат, но когда пытался выполнить оба запроса вместе, я получал эту ошибку.
Пример таблицы и последовательности:
SQL> CREATE TABLE employees(
2 employee_id NUMBER(6) NOT NULL, ---скрипт создания таблицы
3 first_name VARCHAR2(20),
4 last_name VARCHAR2(25) NOT NULL,
5 salary NUMBER(8,2),
6 hiredate DATE,
7 department_id NUMBER(4));
Таблица создана.
SQL> CREATE SEQUENCE emp_seq --Создание последовательности
2 START WITH 1
3 INCREMENT BY 1
4 NOCACHE;
Последовательность создана.
Действительно, ваш insert
не сработает, потому что …
SQL> INSERT INTO employees
2 (employee_id, last_name, hiredate)
3 VALUES ( (SELECT emp_seq.NEXTVAL, 'Smith', sysdate FROM dual) );
VALUES ( (SELECT emp_seq.NEXTVAL, 'Smith', sysdate FROM dual) )
----------------------------------------------------
... все это считается employee_id
*
ОШИБКА на строке 3:
ORA-00947: недостаточно значений
Но если вы сделаете это вот так:
SQL> INSERT INTO employees
2 (employee_id, last_name, hiredate)
3 (SELECT emp_seq.NEXTVAL, 'Smith', sysdate FROM dual);
1 строка создана.
Или так:
SQL> INSERT INTO employees
2 (employee_id, last_name, hiredate)
3 VALUES ( emp_seq.NEXTVAL, 'Jones', sysdate );
1 строка создана.
Тогда это сработает.
SQL> SELECT * FROM employees;
EMPLOYEE_ID FIRST_NAME LAST_NAME SALARY HIREDATE DEPARTMENT_ID
----------- -------------------- ------------------------- ---------- ------------------- -------------
1 Smith 27.10.2024 10:03:44
2 Jones 27.10.2024 10:03:57
SQL>
С другой стороны, если ваша версия базы данных это позволяет (я полагаю/надеюсь, что вы не на 11g или ниже?), вы могли бы использовать столбец идентификатора и позволить Oracle управлять идентификаторами:
SQL> DROP TABLE employees;
Таблица удалена.
SQL> CREATE TABLE employees(
2 employee_id NUMBER(6) GENERATED ALWAYS AS IDENTITY NOT NULL, ---скрипт создания таблицы
3 first_name VARCHAR2(20),
4 last_name VARCHAR2(25) NOT NULL,
5 salary NUMBER(8,2),
6 hiredate DATE,
7 department_id NUMBER(4));
Таблица создана.
SQL> INSERT INTO employees
2 (last_name, hiredate)
3 VALUES ('Smith', sysdate );
1 строка создана.
SQL> SELECT * FROM employees;
EMPLOYEE_ID FIRST_NAME LAST_NAME SALARY HIREDATE DEPARTMENT_ID
----------- -------------------- ------------------------- ---------- ------------------- -------------
1 Smith 27.10.2024 10:09:18
SQL>
Ответ или решение
Ошибка "не хватает значений" в SQL возникает, когда количество значений, предоставляемых для вставки в таблицу, не соответствует количеству колонок, указанных в запросе. В вашей ситуации проблема заключается в использовании встроенной подзапроса в команде VALUES
, что неправильно интерпретируется SQL-сервером.
Проблема и причина
Вы написали следующий запрос для вставки данных:
INSERT INTO employees (employee_id, last_name, hiredate)
VALUES ((SELECT emp_seq.nextval, 'Smith', SYSDATE FROM dual));
Однако, оператор VALUES
в данном контексте ожидает один набор значений, но подзапрос возвращает три значения: emp_seq.nextval
, 'Smith'
и SYSDATE
. В результате SQL-сервер сообщает об ошибке "не хватает значений" (или ORA-00947).
Правильные подходы
Чтобы правильно выполнить вставку данных, у вас есть несколько вариантов:
- Использование оператора SELECT:
Вы можете заменить VALUES
на SELECT
, который будет генерировать строку для вставки:
INSERT INTO employees (employee_id, last_name, hiredate)
SELECT emp_seq.NEXTVAL, 'Smith', SYSDATE FROM dual;
В этом случае запрос будет работать, поскольку оператор SELECT
возвращает одну строку с тремя значениями, как требуется.
- Использование
VALUES
с прямыми значениями:
Если параметр employee_id
берётся из последовательности, вы можете непосредственно обратиться к последовательности при вставке:
INSERT INTO employees (employee_id, last_name, hiredate)
VALUES (emp_seq.NEXTVAL, 'Jones', SYSDATE);
Данный подход также корректен и дает ожидаемый результат.
Рекомендации
- Использование идентификаторов: Если ваша версия базы данных позволяет, рассмотрите возможность использования столбца идентичности. Это упростит код, поскольку вам не придется вручную управлять значениями идентификатора. Пример создания таблицы с идентификатором:
CREATE TABLE employees (
employee_id NUMBER(6) GENERATED ALWAYS AS IDENTITY NOT NULL,
first_name VARCHAR2(20),
last_name VARCHAR2(25) NOT NULL,
salary NUMBER(8,2),
hiredate DATE,
department_id NUMBER(4)
);
С таким определением таблицы вы сможете вставлять данные без явного указания идентификатора:
INSERT INTO employees (last_name, hiredate)
VALUES ('Smith', SYSDATE);
Заключение
При работе с SQL важно понимать, как структура ваших запросов влияет на их выполнение. При использовании подзапросов следует быть внимательным, чтобы возвращаемое число значений соответствовало ожидаемому количеству. Альтернативные подходы, такие как использование подзапроса или вызова последовательности напрямую, могут значительно упростить процесс.