Hello im trying to create a stock database for cars having 2 tables, carros and faturas, soo carros is the name of the car(veiculo) and how many cars i have in stock(quantidade) and how many cars i sold(vendidos), and the table faturas has the id and the car name(veiculo) the rest doesnt matter of my objective.
My objective is after i insert on table faturas the car name(veiculo) it goes to the carros table and it adds +1 to the vendidos of that car, but for now it is updating all my vendidos row.
Here it goes my code and i hope you can help me, Thanks.
CREATE TABLE carros (
veiculo VARCHAR(10) not null,
quantidade INTEGER not null,
vendidos INTEGER,
PRIMARY KEY (veiculo));
CREATE TABLE faturas (
id serial not null,
veiculo VARCHAR(10) NOT NULL,
matricula VARCHAR(10) NOT NULL,
nome CHAR(30) NOT NULL,
contacto VARCHAR(15) NOT NULL,
valor INT NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (veiculo) REFERENCES carros(veiculo));
CREATE OR REPLACE FUNCTION public.stockupdate()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
begin
IF EXISTS(select c.veiculo from carros c, faturas f where c.veiculo = f.veiculo limit 1) THEN
UPDATE carros SET vendidos = vendidos + 1 from faturas where carros.veiculo = faturas.veiculo;
end if;
return new;
end;
$function$
;
CREATE OR REPLACE FUNCTION public.stockupdate()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
begin
IF EXISTS(select c.veiculo from carros c, faturas f where c.veiculo = f.veiculo limit 1) THEN
UPDATE carros SET vendidos = vendidos + 1 from faturas where carros.veiculo = faturas.veiculo;
end if;
return new;
end;
$function$
;
create trigger stockupdatetrigger
before insert
on faturas
for each row
execute procedure stockupdate();
INSERT INTO carros
(veiculo, quantidade, vendidos)
VALUES('Opel', 10, 0);
INSERT INTO carros
(veiculo, quantidade, vendidos)
VALUES('Fiat', 10, 0);
INSERT INTO public.faturas
(veiculo, matricula, nome, contacto, valor)
VALUES('Opel', 'ABC 123', 'Ruben', '226-255243', 15000);
CREATE TABLE faturas (
id serial not null,
veiculo VARCHAR(10) NOT NULL,
matricula VARCHAR(10) NOT NULL,
nome CHAR(30) NOT NULL,
contacto VARCHAR(15) NOT NULL,
valor INT NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (veiculo) REFERENCES carros(veiculo)
);
CREATE OR REPLACE FUNCTION public.stockupdate()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
begin
IF EXISTS(select c.veiculo from carros c, faturas f where c.veiculo = f.veiculo limit 1) THEN
UPDATE carros SET vendidos = vendidos + 1 from faturas where carros.veiculo = faturas.veiculo;
end if;
return new;
end;
$function$
;
create trigger stockupdatetrigger
before insert
on faturas
for each row
execute procedure stockupdate();
INSERT INTO carros
(veiculo, quantidade, vendidos)
VALUES('Opel', 10, 0);
INSERT INTO carros
(veiculo, quantidade, vendidos)
VALUES('Fiat', 10, 0);
INSERT INTO public.faturas
(veiculo, matricula, nome, contacto, valor)
VALUES('Opel', 'ABC 123', 'Ruben', '226-255243', 15000);
You seem to be overcomplicating things here. I think the trigger function you wanted to write is just:
create or replace function public.stockupdate()
returns trigger
language plpgsql
as $$
begin
update carros set vendidos = vendidos + 1 where veiculo = new.veiculo;
return new;
end;
$$;
Pseudo-table new can be used to access the row that is being inserted. You can use it to filter the carros table and update the correspoding record, if any
Related
I have created a database for an airline that currently has 3 tables: Table1: FLIGHTS, Table2: Clients, Table3: Reservation, and I want to create a function that returns the time left in the month from the reservation made till the flights date inside a function. After I run it, nothing is returning. Any tips?
DROP DATABASE if exists Chartered_Airlines;
CREATE DATABASE Chartered_Airlines;
USE Chartered_Airlines;
CREATE TABLE FLIGHTS(FLIGHT_NO INT(5) NOT NULL, DEPARTURE VARCHAR(15), ARRIVAL VARCHAR(15), TYPEFL VARCHAR(15), SEATS INT(4) NOT NULL, FREE_SEATS INT(4), FLIGHT_DATE DATE,
PRIMARY KEY(FLIGHT_NO));
CREATE TABLE CUSTOMERS(CL_NO INT(5) NOT NULL, LAST_NAME VARCHAR(15) NOT NULL, FIRST_NAME VARCHAR(15) NOT NULL, CITIZENSHIP VARCHAR(15) NOT NULL, B_DATE DATE,
PRIMARY KEY(CL_NO));
CREATE TABLE RESERVATIONS(RES_NO INT(5) NOT NULL, CL_NO INT(5) NOT NULL, FLIGHT_NO INT(5), COST FLOAT(15), RES_DATE DATE,
PRIMARY KEY(RES_NO),
FOREIGN KEY(FLIGHT_NO) REFERENCES FLIGHTS(FLIGHT_NO),
FOREIGN KEY(CL_NO) REFERENCES CUSTOMERS(CL_NO));
SHOW TABLES;
INSERT INTO FLIGHTS VALUES(10,'ATHENS','LONDON','EXTERNAL',310,34, '2023/10/04');
INSERT INTO FLIGHTS VALUES(20,'ATHENS','MYKONOS','INTERNAL',100,5, '2022/12/12');
INSERT INTO FLIGHTS VALUES(30,'ATHENS','PARIS','EXTERNAL',256,16, '2022/09/07');
INSERT INTO CUSTOMERS VALUES(5472,'ALEKSANDRAKIS','GEORGIOS','GREECE','1989/01/01');
INSERT INTO CUSTOMERS VALUES(1354,'STAVROU','ANTREAS','GREECE','1977/07/07');
INSERT INTO CUSTOMERS VALUES(6598,'AMALFIO','ROBERTO','ITALY','1995/05/02');
INSERT INTO CUSTOMERS VALUES(1953,'EUSTATHIOU','NIKOS','GREECE','2001/06/03');
INSERT INTO CUSTOMERS VALUES(1387,'STAKAS','VASILLIS','GREECE','1990/01/07');
INSERT INTO RESERVATIONS VALUES(3174,5472,10,6482, '2020/03/02');
INSERT INTO RESERVATIONS VALUES(3143,1354,10,6482, '2021/05/09');
INSERT INTO RESERVATIONS VALUES(1286,6598,20,662, '2022/10/12');
INSERT INTO RESERVATIONS VALUES(3275,1953,20,662, '2022/09/08');
INSERT INTO RESERVATIONS VALUES(7654,1387,30,264, '2022/09/06');
SELECT * FROM FLIGHTS;
SELECT * FROM CUSTOMERS;
SELECT * FROM RESERVATIONS;
DESC FLIGHTS;
DESC RESERVATIONS;
DESC CUSTOMERS;
DELIMITER //
CREATE FUNCTION TIME_LEFT(RES_NO INT) RETURNS INT
DETERMINISTIC
BEGIN
DECLARE TIME_LEFT INT DEFAULT 0;
SELECT TIMESTAMPDIFF(MONTH,RESERVATIONS.RES_DATE,FLIGHTS.FLIGHT_DATE) INTO TIME_LEFT FROM RESERVATIONS
WHERE RES_NO = RES_NO AND RESERVATIONS.FLIGHT_NO = FLIGHTS.FLIGHT_NO ;
RETURN TIME_LEFT;
END;
//
SELECT TIME_LEFT(3143);
I was expecting that it returns the date difference between reservation made date and flight date
Your forgot to use join and also use a variable name for holding the value passed in function:
DELIMITER //
CREATE FUNCTION TIME_LEFT(VAR_RES_NO INT) RETURNS INT
DETERMINISTIC
BEGIN
DECLARE TIME_LEFT INT DEFAULT 0;
SELECT TIMESTAMPDIFF(MONTH,RESERVATIONS.RES_DATE,FLIGHTS.FLIGHT_DATE) INTO TIME_LEFT FROM RESERVATIONS join FLIGHTS
WHERE RES_NO = VAR_RES_NO AND RESERVATIONS.FLIGHT_NO = FLIGHTS.FLIGHT_NO ;
RETURN TIME_LEFT;
END;
//
I think you need to include the table Flights in the SELECT statement, as well as specify the difference between RES_NO in the input vs reservations e.g.,
CREATE FUNCTION TIME_LEFT(RES_NO_in INT) RETURNS INT
DETERMINISTIC
BEGIN
DECLARE TIME_LEFT INT DEFAULT 0;
SELECT TIMESTAMPDIFF(MONTH,RESERVATIONS.RES_DATE,FLIGHTS.FLIGHT_DATE) INTO TIME_LEFT
FROM RESERVATIONS, FLIGHTS
WHERE RESERVATIONS.RES_NO = RES_NO_in AND RESERVATIONS.FLIGHT_NO = FLIGHTS.FLIGHT_NO;
RETURN TIME_LEFT;
END;
or
CREATE FUNCTION TIME_LEFT(RES_NO_in INT) RETURNS INT
DETERMINISTIC
BEGIN
DECLARE TIME_LEFT INT DEFAULT 0;
SELECT TIMESTAMPDIFF(MONTH,RESERVATIONS.RES_DATE,FLIGHTS.FLIGHT_DATE) INTO TIME_LEFT
FROM RESERVATIONS
INNER JOIN FLIGHTS ON RESERVATIONS.FLIGHT_NO = FLIGHTS.FLIGHT_NO
WHERE RESERVATIONS.RES_NO = RES_NO_in;
RETURN TIME_LEFT;
END;
(zaposlenik means emplpoyee, godisnji_odmor means vacation)
So I want to make a trigger that triggers if an employee(here is called zaposlenik) has more than 21 days of vacation after an insert in year 2020. An employee can have multiple vacations(like 2 days, 10 days...) one to many relation between tables zaposlenik(employee) and godisnji_odmor(vacation). My problem here is that I don't know how to get that zaposlenik_id from the insert on which the trigger tiggers so I can sum up the vacation days of that employee on the ID = zaposlenik_id.
Here are my tables and trigger.
CREATE TABLE zaposlenik
(
zaposlenik_id INTEGER CONSTRAINT zaposlenik_pk PRIMARY KEY,
posao VARCHAR(30) NOT NULL,
ime VARCHAR(30) NOT NULL,
prezime VARCHAR(30) NOT NULL,
broj_tel INTEGER NOT NULL,
email VARCHAR(50) NOT NULL,
adresa VARCHAR(100) NOT NULL,
mjesecni_iznos_place FLOAT NOT NULL,
IBAN VARCHAR(34) NOT NULL,
budzet FLOAT,
parking_mjesto_id VARCHAR(5) CONSTRAINT zaposlenik_parking_mjesto_fk REFERENCES
parking_mjesto(etaza_i_br_mjesta),
zaposlenik_id_2 INTEGER CONSTRAINT zaposlenik_zaposlenik_fk REFERENCES
zaposlenik(zaposlenik_id)
);
CREATE TABLE godisnji_odmor
(
godisnji_odmor_id INTEGER CONSTRAINT godisnji_odmor_pk PRIMARY KEY,
pocetak DATE NOT NULL,
kraj DATE NOT NULL,
zaposlenik_id INTEGER NOT NULL CONSTRAINT godisnji_odmor_zaposlenik_fk REFERENCES
zaposlenik(zaposlenik_id)
);
CREATE OR REPLACE TRIGGER t_godisnji
AFTER INSERT
ON godisnji_odmor
DECLARE
v_br NUMBER; --sum of the vacation days
v_id NUMBER; -- id of the employee that is inserted (his id is zaposlenik_id)
BEGIN
SELECT zaposlenik_id INTO v_id FROM INSERTED;
SELECT SUM( g.kraj - g.pocetak ) INTO v_br
FROM zaposlenik z INNER JOIN godisnji_odmor g USING(zaposlenik_id)
WHERE g.pocetak > '01-JANUARY-2020' AND zaposlenik_id = v_id;
IF v_br > 21 THEN
ROLLBACK;
raise_application_error(-20100,'Godisnji prekoracuje 21 dan u sumi');
END IF;
END t_godisnji;
/
You need to use the :new for this as follows:
replace
SELECT zaposlenik_id INTO v_id FROM INSERTED;
with
v_id := :new.zaposlenik_id;
In short, You can have your code without v_id variable as follows:
CREATE OR REPLACE TRIGGER t_godisnji
AFTER INSERT
ON godisnji_odmor
DECLARE
v_br NUMBER; --sum of the vacation days
--v_id NUMBER; -- id of the employee that is inserted (his id is zaposlenik_id)
BEGIN
--SELECT zaposlenik_id INTO v_id FROM INSERTED;
SELECT SUM( g.kraj - g.pocetak ) INTO v_br
FROM zaposlenik z INNER JOIN godisnji_odmor g USING(zaposlenik_id)
WHERE g.pocetak > '01-JANUARY-2020'
AND zaposlenik_id = :new.zaposlenik_id; -- see the usage of :new here
IF v_br > 21 THEN
ROLLBACK;
raise_application_error(-20100,'Godisnji prekoracuje 21 dan u sumi');
END IF;
END t_godisnji;
/
CREATE TABLE emp (
empname text NOT NULL,
salary integer
);
CREATE TABLE emp_audit(
operation char(1) NOT NULL,
stamp timestamp NOT NULL,
userid text NOT NULL,
empname text NOT NULL,
salary integer
);
CREATE OR REPLACE FUNCTION process_emp_audit() RETURNS TRIGGER AS $emp_audit$
BEGIN
--
-- Create a row in emp_audit to reflect the operation performed on emp,
-- make use of the special variable TG_OP to work out the operation.
--
IF (TG_OP = 'DELETE') THEN
INSERT INTO emp_audit SELECT 'D', now(), user_id, OLD.*;
RETURN OLD;
ELSIF (TG_OP = 'UPDATE') THEN
INSERT INTO emp_audit SELECT 'U', now(), user_id, OLD.*;
RETURN NEW;
ELSIF (TG_OP = 'INSERT') THEN
INSERT INTO emp_audit SELECT 'I', now(), user_id, NEW.*;
RETURN NEW;
END IF;
RETURN NULL; -- result is ignored since this is an AFTER trigger
END;
$emp_audit$ LANGUAGE plpgsql;
DROP TRIGGER emp_audit ON emp;
CREATE TRIGGER emp_audit
AFTER INSERT OR UPDATE OR DELETE ON emp
FOR EACH ROW EXECUTE PROCEDURE process_emp_audit();
user_id not in emp table
can we send parameters to trigger function which is not related to affected table in PostgreSQL. if I enter an active record of user there may some issue as if two different users active then trigger will confuse
Create table statements:
CREATE TABLE Client(
nclient INT NOT NULL,
nom VARCHAR(45) NOT NULL UNIQUE,
plafond NUMERIC(10,2) DEFAULT 0,
CHECK (plafond>=0),
CONSTRAINT PK_Client PRIMARY KEY (nclient)
);
CREATE TABLE Compte(
ncompte INT NOT NULL,
solde NUMERIC(10,2),
ouvert NUMERIC(1) DEFAULT 1,
nbessais INT,
code Numeric(4),
nclient NUMERIC(4),
CONSTRAINT PK_Compte PRIMARY KEY (ncompte),
CONSTRAINT FK_Compte_nclient_Client FOREIGN KEY (nclient) REFERENCES Client(nclient),
CONSTRAINT CK_solde CHECK (solde>=0),
CONSTRAINT CK_ouvert CHECK(ouvert between 0 and 1),
CONSTRAINT CK_code CHECK (code>0)
);
CREATE TABLE Operation(
noperation INT NOT NULL,
dateoperation DATE Default SYSDATE,
codepropose NUMERIC(4),
montant NUMERIC(10,2),
ncompte INT,
CONSTRAINT PK_Operation PRIMARY KEY (noperation),
CONSTRAINT FK_Ope_ncompte_Compte FOREIGN KEY (ncompte) REFERENCES Compte(ncompte),
CONSTRAINT CK_codepropose CHECK (codepropose>=0),
CONSTRAINT CK_montant CHECK (montant>=0)
);
CREATE TABLE Historique(
nhistorique INT NOT NULL,
dateoperation DATE DEFAULT SYSDATE,
montant NUMERIC(10,2),
ncompte INT,
CONSTRAINT PK_Historique PRIMARY KEY (nhistorique)
);
CREATE TABLE Incident(
nincident INT NOT NULL,
message VARCHAR(45),
noperation INT,
CONSTRAINT PK_Incident PRIMARY KEY (nincident),
CONSTRAINT FK_Inc_noperation_Operation FOREIGN KEY (noperation) REFERENCES Operation(noperation)
);
Trigger code:
create or replace TRIGGER TRG_OPERATION_CARTE
AFTER INSERT OR UPDATE ON Operation
FOR EACH ROW
DECLARE
solde NUMBER := 0;
ouvert NUMBER := 0;
essai NUMBER := 0;
code NUMBER := 0;
plafondActuel NUMBER := 0;
decouvertAuth NUMBER := 0;
BEGIN
SELECT SUM(montant) INTO plafondActuel FROM Operation WHERE dateoperation >= SYSDATE - 7 AND ncompte = :NEW.ncompte;
SELECT plafond INTO decouvertAuth FROM Client Cl LEFT JOIN Compte Cp ON Cl.nclient = Cp.nclient WHERE Cp.ncompte = :NEW.ncompte;
SELECT solde, ouvert, nbessais, code
INTO solde, ouvert, essai, code
FROM Compte
WHERE Compte.ncompte = :NEW.ncompte;
IF ouvert = 0 THEN
INSERT INTO Incident (message,noperation) values ('Compte bloqué',:NEW.noperation);
ELSE
IF :NEW.codepropose = code THEN
IF (solde + decouvertAuth) > :NEW.montant THEN
IF (plafondActuel + :NEW.montant) > 300 THEN
INSERT INTO Incident (message,noperation) values ('Lopération dépasse le plafond authorisé',:NEW.noperation);
ELSE
INSERT INTO Historique (montant,ncompte) values (:NEW.montant,:NEW.ncompte);
END IF;
ELSE
INSERT INTO Incident (message,noperation) values ('Lopération dépasse le découvert authorisé',:NEW.noperation);
END IF;
ELSE
essai := essai + 1;
UPDATE Compte SET nbessais = essai WHERE Compte.ncompte = :NEW.ncompte;
IF essai >=3 THEN
UPDATE Compte SET ouvert = 0 WHERE Compte.ncompte = :NEW.ncompte;
INSERT INTO Incident (message,noperation) values ('Compte bloqué, 3 tentatives échouées',:NEW.noperation);
END IF;
END IF;
END IF;
END;
I am pretty sure that the algorithm and the Trigger are correct but I have an error :
Cause: A trigger (or a user defined plsql function that is referenced in this statement) attempted to look at (or modify) a table that was in the middle of being modified by the statement which fired it.
Action: Rewrite the trigger (or function) so it does not read that table.
Here are some tests, everything is ok apart from the last insert :
INSERT INTO Client (nom) values ('abderrahmane');
INSERT INTO Client (nom) values ('ben');
Select * FROM Client;
INSERT INTO Compte (solde,nclient) values (200,1);
INSERT INTO Compte (nclient,code) values (2,2403);
INSERT INTO Compte (solde,nclient) values (2300,3);
SELECT * FROM Compte;
INSERT INTO Operation (montant,ncompte) values (200,2);
INSERT INTO Operation (codepropose,ncompte) values (2010,2);
INSERT INTO Operation (ncompte) values (1);
INSERT INTO Operation (codepropose,montant,ncompte) values (2020,20,1);
INSERT INTO Operation (codepropose,montant,ncompte,dateoperation) values (2030,30,2,'01/01/2001');
INSERT INTO Operation (codepropose,montant,ncompte) values (2030,30,3);
SELECT * FROM Operation;
INSERT INTO Historique (montant,ncompte) values (200,2);
INSERT INTO Historique (dateoperation,ncompte) values ('15/04/16',2);
INSERT INTO Historique (ncompte) values (1);
INSERT INTO Historique (dateoperation,montant,ncompte) values ('15/04/16',20,1);
INSERT INTO Historique (dateoperation,montant,ncompte) values ('15/04/16',30,3);
SELECT * FROM Historique;
INSERT INTO Incident (message,noperation) values ('Incident 1',2);
INSERT INTO Incident (message,noperation) values ('Incident 2',8);
INSERT INTO Incident (message,noperation) values ('Incident 3',1);
SELECT * FROM Incident;
UPDATE Compte SET ouvert = 0 WHERE nclient = 2;
INSERT INTO Operation (montant,ncompte,codepropose) values (200,2,2403);
SELECT * FROM Incident;
Mutating Table error comes because your are using that particular table and making any kind of modification in the same table.
Since you are trying to select records from table in the trigger of the same table you are getting this error.
Thanks everyone for your answers, I just had to change the tablename, instead of taking the sum from operation, I took it from historique :
CREATE OR REPLACE TRIGGER TRG_OPERATION_CARTE
AFTER INSERT OR UPDATE ON Operation
FOR EACH ROW
DECLARE
solde NUMBER := 0;
ouvert NUMBER := 0;
essai NUMBER := 0;
code NUMBER := 0;
plafondActuel NUMBER := 0;
decouvertAuth NUMBER := 0;
BEGIN
SELECT SUM(montant) INTO plafondActuel FROM Historique WHERE dateoperation >= SYSDATE - 7 AND ncompte = :NEW.ncompte;
SELECT plafond INTO decouvertAuth FROM Client Cl LEFT JOIN Compte Cp ON Cl.nclient = Cp.nclient WHERE Cp.ncompte = :NEW.ncompte;
SELECT solde, ouvert, nbessais, code
INTO solde, ouvert, essai, code
FROM Compte
WHERE Compte.ncompte = :NEW.ncompte;
IF ouvert = 0 THEN
INSERT INTO Incident (message,noperation) values ('Compte bloqué',:NEW.noperation);
ELSE
IF :NEW.codepropose = code THEN
IF (solde + decouvertAuth) >= :NEW.montant THEN
IF (plafondActuel + :NEW.montant) > 300 THEN
INSERT INTO Incident (message,noperation) values ('Lopération dépasse le plafond authorisé',:NEW.noperation);
ELSE
UPDATE Compte SET solde = (solde - :NEW.montant) WHERE Compte.ncompte = :NEW.ncompte;
INSERT INTO Historique (montant,ncompte) values (:NEW.montant,:NEW.ncompte);
END IF;
ELSE
INSERT INTO Incident (message,noperation) values ('Lopération dépasse le découvert authorisé',:NEW.noperation);
END IF;
ELSE
essai := essai + 1;
UPDATE Compte SET nbessais = essai WHERE Compte.ncompte = :NEW.ncompte;
IF essai >=3 THEN
UPDATE Compte SET ouvert = 0 WHERE Compte.ncompte = :NEW.ncompte;
INSERT INTO Incident (message,noperation) values (CONCAT('Compte bloqué : tentative n° ', essai),:NEW.noperation);
ELSE
INSERT INTO Incident (message,noperation) values (CONCAT('Code erroné : tentative n° ', essai),:NEW.noperation);
END IF;
END IF;
END IF;
END;
/
I have a postgresql table with sequence:
CREATE TABLE A (
id integer NOT NULL DEFAULT nextval('a_seq'::regclass),
X integer,
Y integer,
Z boolean default false,
CONSTRAINT A_pkey PRIMARY KEY (id)
)
I have an insert statment in function as follows:
insert into A(x,y) select $1,getdig();
i want this insert to return the id the row was given to a function varabile called A_id
It should be something like:
CREATE OR REPLACE FUNCTION bbb(m integer)
RETURNS integer AS
$BODY$
declare
A_id int;
begin
insert into A(x,y) select $1,getdig() RETURNING id into A_id;
actions using A_id like:
update A set z=True where id=A_id;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
How do I do that?
There is no need for the select:
CREATE OR REPLACE FUNCTION bbb(m integer)
RETURNS integer AS
$BODY$
declare
A_id int;
begin
insert into A(x,y)
values ($1,getdig())
RETURNING id into A_id;
-- actions using A_id like:
update A set z=True where id=A_id;
return a_id; -- don't forget to return something!
end;
$BODY$
LANGUAGE plpgsql VOLATILE
You use the returning clause:
with i as (
insert into A(x,y)
select $1, getdig()
returning id
)
select *
from i;
Technically, the CTE is not necessary. But I prefer that queries that return values start with SELECT.