I've seen questions for checking if a row merely exists, But I haven't seen anything (on SO or elsewhere) about whether or not all the data is filled in.
I was hoping that SELECT true FROM myTable WHERE name='myRow' AND * IS NOT NULL; would work, but it doesn't.
What wildcard will work in place of the asterisk, if there is one? Will I have to put in each column name into the query individually?
You can indeed reference the whole row, but not using *, but by using the table name:
SELECT true
FROM myTable
WHERE name='myRow'
AND myTable IS NOT NULL;
The IS NOT NULL operator on a row value returns true if all columns of the row are not null.
The following statement:
with mytable (col1, col2, col3) as (
values
(1,null,null),
(null,1,null),
(null,null,1),
(1,1,1)
)
select *
from mytable
where mytable is not null;
will return:
col1 | col2 | col3
-----+------+-----
1 | 1 | 1
The opposite btw. is not true. where mytable is null will not return anything because a row is by definition never null (because then it wouldn't exist). To find rows where at least one column is null you would need to use where not (mytable is not null)
A similar problem is described here: https://dba.stackexchange.com/q/143959/1822
Related
I'm still having trouble understanding how CTE works.
I'm looking to make an insert. In case of conflict I use the on conflict do nothing but I want it to return the id to me (for the success of the insert or the conflict)
WITH inserted AS (
INSERT INTO fiche(label)
VALUES ('label')
ON CONFLICT (label) DO NOTHING
RETURNING *
)
SELECT * FROM inserted
WHERE NOT EXISTS (SELECT 1 FROM inserted);
Note that
SELECT * FROM some_relation
WHERE NOT EXISTS (SELECT 1 FROM some_relation);
will always give you an empty result. Either some_relation is empty itself or if it is not empty SELECT 1 FROM some_relation is not empty and therefore NOT EXISTS ... always returns false and so no record is matching the WHERE clause.
What you want is to have the VALUES as a CTE. You can then reference the values from your INSERT statement and in a SELECT to compare those values to the result of the RETURNING clause.
WITH
vals AS (
VALUES ('label')
),
inserted AS (
INSERT INTO fiche(label)
SELECT * FROM vals
ON CONFLICT (label) DO NOTHING
RETURNING label, id
)
SELECT
vals.column1,
inserted.id
FROM vals
LEFT JOIN inserted ON vals.column1 = inserted.label
This should give you a row for each row in your VALUES clause and the second column will be NULL if it was not inserted due to a conflict or the inserted ID otherwise.
I have the following table in Postgres 11.
col1 col2 source col3
a abc curation rejected
a abc DB
b etg DB accepted
c jfh curation
How can I assign value in col3 based on the values in col1
The expected output is:
col1 col2 source col3
a abc curation rejected
a abc DB rejected
b etg DB accepted
c jfh curation null
Is there a way to check if values in col1 and col2 in subsequent rows are identical, then assign same col3 to all the rows (if the col3 value in other row is null).
Any help is highly appreciated.
You're not entirely clear on what the criteria is, but at a basic level it could depend on how you want to query this data, there are multiple ways you could do this.
Generated Columns
drop table if exists atable ;
CREATE TABLE atable (
cola text ,
colb text GENERATED ALWAYS AS (case when cola='a' then 'rejected' else
null end) STORED
);
insert into atable(cola) values ('a')
A View.
create or replace view aview as
select cola, case when cola='a' then 'rejected' else null end as colb
from atable;
Both would yield the same results.
cola|colb |
----+--------+
a |rejected|
Other options could be a materialized view, simple query logic.
You have options.
update a2 set
col3 =
case when col1 = 'a' then 'rejected'
when col1 = 'b' then 'accepted'
when col1 = 'c' then 'null' end
where col3 is null
returning *;
You can also set triggers. But generated columns only available from 12. So you need upgrade to use generated columns.
db fiddle
I want the rows in which there is at least one column with a null value. I've tried to use a row expression like:
SELECT *
FROM <table>
WHERE <table> IS NULL
But it does not work. Is my query incorrect or?
PS I am using version 13.4
You can reference the table alias in the WHERE clause. The condition where the_table is not null would return the rows where all columns are not null.
The opposite of that (where at least one column is null) can be achieved by negating the expression:
select *
from the_table
where not (the_table is not null);
Looks a bit strange, but it's not the same as the_table is null - which is never true, as the reference to the table (alias) refers to an existing row. And if a row exists the the "whole row" can't be null.
This:
with the_table (col1, col2, col3) as (
values
(1,null,null),
(null,2,null),
(null,3,4),
(5,6,7)
)
select *
from the_table
where not (the_table is not null);
returns:
col1 | col2 | col3
-----+------+-----
1 | |
| 2 |
| 3 | 4
to check if the value is not null, you use the IS NOT NULL operator
value IS NOT NULL
The expression returns true if the value is NULL or false if it is not.
You need to mention your column name in the search.
SELECT * FROM <table_name> WHERE <column_name> IS NULL;
For more than one columns:
SELECT * FROM <table_name> WHERE <column1_name> IS NULL OR <column2_name> IS NULL;
I have the following table:
RecordID
Name
Col1
Col2
....
ColN
The RecordID is BIGINT PRIMARY KEY CLUSTERED IDENTITY(1,1) and RecordID and Name are initialized. The other columns are NULLs.
I have a function which returns information about the other columns by Name.
To initialized my table I use the following algorithm:
Create a LOOP
Get a row, select its Name value
Execute the function using the selected name, and store its result
in temp variables
Insert the temp variables in the table
Move to the next record
Is there a way to do this without looping?
Cross apply was basically built for this
SELECT D.deptid, D.deptname, D.deptmgrid
,ST.empid, ST.empname, ST.mgrid
FROM Departments AS D
CROSS APPLY fn_getsubtree(D.deptmgrid) AS ST;
Using APPLY
UPDATE some_table
SET some_row = another_row,
some_row2 = another_row/2
FROM some_table st
CROSS APPLY
(SELECT TOP 1 another_row FROM another_table at WHERE at.shared_id=st.shared_id)
WHERE ...
using cross apply in an update statement
You can simply say the following if you already have the records in the table.
UPDATE MyTable
SET
col1 = dbo.col1Method(Name),
col2 = dbo.col2Method(Name),
...
While inserting new records, assuming RecordID is auto-generated, you can say
INSERT INTO MyTable(Name, Col1, Col2, ...)
VALUES(#Name, dbo.col1Method(#Name), dbo.col2Method(#name), ...)
where #Name contains the value for the Name column.
I'm using PostgreSQL 9.0 and I have a table with just an artificial key (auto-incrementing sequence) and another unique key. (Yes, there is a reason for this table. :)) I want to look up an ID by the other key or, if it doesn't exist, insert it:
SELECT id
FROM mytable
WHERE other_key = 'SOMETHING'
Then, if no match:
INSERT INTO mytable (other_key)
VALUES ('SOMETHING')
RETURNING id
The question: is it possible to save a round-trip to the DB by doing both of these in one statement? I can insert the row if it doesn't exist like this:
INSERT INTO mytable (other_key)
SELECT 'SOMETHING'
WHERE NOT EXISTS (SELECT * FROM mytable WHERE other_key = 'SOMETHING')
RETURNING id
... but that doesn't give the ID of an existing row. Any ideas? There is a unique constraint on other_key, if that helps.
Have you tried to union it?
Edit - this requires Postgres 9.1:
create table mytable (id serial primary key, other_key varchar not null unique);
WITH new_row AS (
INSERT INTO mytable (other_key)
SELECT 'SOMETHING'
WHERE NOT EXISTS (SELECT * FROM mytable WHERE other_key = 'SOMETHING')
RETURNING *
)
SELECT * FROM new_row
UNION
SELECT * FROM mytable WHERE other_key = 'SOMETHING';
results in:
id | other_key
----+-----------
1 | SOMETHING
(1 row)
No, there is no special SQL syntax that allows you to do select or insert. You can do what Ilia mentions and create a sproc, which means it will not do a round trip fromt he client to server, but it will still result in two queries (three actually, if you count the sproc itself).
using 9.5 i successfully tried this
based on Denis de Bernardy's answer
only 1 parameter
no union
no stored procedure
atomic, thus no concurrency problems (i think...)
The Query:
WITH neworexisting AS (
INSERT INTO mytable(other_key) VALUES('hello 2')
ON CONFLICT(other_key) DO UPDATE SET existed=true -- need some update to return sth
RETURNING *
)
SELECT * FROM neworexisting
first call:
id|other_key|created |existed|
--|---------|-------------------|-------|
6|hello 1 |2019-09-11 11:39:29|false |
second call:
id|other_key|created |existed|
--|---------|-------------------|-------|
6|hello 1 |2019-09-11 11:39:29|true |
First create your table ;-)
CREATE TABLE mytable (
id serial NOT NULL,
other_key text NOT NULL,
created timestamptz NOT NULL DEFAULT now(),
existed bool NOT NULL DEFAULT false,
CONSTRAINT mytable_pk PRIMARY KEY (id),
CONSTRAINT mytable_uniq UNIQUE (other_key) --needed for on conflict
);
you can use a stored procedure
IF (SELECT id FROM mytable WHERE other_key = 'SOMETHING' LIMIT 1) < 0 THEN
INSERT INTO mytable (other_key) VALUES ('SOMETHING')
END IF
I have an alternative to Denis answer, that I think is less database-intensive, although a bit more complex:
create table mytable (id serial primary key, other_key varchar not null unique);
WITH table_sel AS (
SELECT id
FROM mytable
WHERE other_key = 'test'
UNION
SELECT NULL AS id
ORDER BY id NULLS LAST
LIMIT 1
), table_ins AS (
INSERT INTO mytable (id, other_key)
SELECT
COALESCE(id, NEXTVAL('mytable_id_seq'::REGCLASS)),
'test'
FROM table_sel
ON CONFLICT (id) DO NOTHING
RETURNING id
)
SELECT * FROM table_ins
UNION ALL
SELECT * FROM table_sel
WHERE id IS NOT NULL;
In table_sel CTE I'm looking for the right row. If I don't find it, I assure that table_sel returns at least one row, with a union with a SELECT NULL.
In table_ins CTE I try to insert the same row I was looking for earlier. COALESCE(id, NEXTVAL('mytable_id_seq'::REGCLASS)) is saying: id could be defined, if so, use it; whereas if id is null, increment the sequence on id and use this new value to insert a row. The ON CONFLICT clause assure
that if id is already in mytable I don't insert anything.
At the end I put everything together with a UNION between table_ins and table_sel, so that I'm sure to take my sweet id value and execute both CTE.
This query needs to search for the value other_key only once, and is a "search this value" not a "check if this value not exists in the table", that is very heavy; in Denis alternative you use other_key in both types of searches. In my query you "check if a value not exists" only on id that is a integer primary key, that, for construction, is fast.
Minor tweak a decade late to Denis's excellent answer:
-- Create the table with a unique constraint
CREATE TABLE mytable (
id serial PRIMARY KEY
, other_key varchar NOT NULL UNIQUE
);
WITH new_row AS (
-- Only insert when we don't find anything, avoiding a table lock if
-- possible.
INSERT INTO mytable ( other_key )
SELECT 'SOMETHING'
WHERE NOT EXISTS (
SELECT *
FROM mytable
WHERE other_key = 'SOMETHING'
)
RETURNING *
)
(
-- This comes first in the UNION ALL since it'll almost certainly be
-- in the query cache. Marginally slower for the insert case, but also
-- marginally faster for the much more common read-only case.
SELECT *
FROM mytable
WHERE other_key = 'SOMETHING'
-- Don't check for duplicates to be removed
UNION ALL
-- If we reach this point in iteration, we needed to do the INSERT and
-- lock after all.
SELECT *
FROM new_row
) LIMIT 1 -- Just return whatever comes first in the results and allow
-- the query engine to cut processing short for the INSERT
-- calculation.
;
The UNION ALL tells the planner it doesn't have to collect results for de-duplication. The LIMIT 1 at the end allows the planner to short-circuit further processing/iteration once it knows there's an answer available.
NOTE: There is a race condition present here and in the original answer. If the entry does not already exist, the INSERT will fail with a unique constraint violation. The error can be suppressed with ON CONFLICT DO NOTHING, but the query will return an empty set instead of the new row. This is a difficult problem because getting that info from another transaction would violate the I in ACID.