I have a POstgreSQL 8.4.
I have a table and i want to find a string in one row (character varying datatype) of this table using substring (character varying datatype) returned by subquery:
SELECT uchastki.kadnum
FROM uchastki
WHERE kadnum LIKE (
SELECT str
FROM test
WHERE str IS NOT NULL)
But get a error
ERROR: more than one row returned by a subquery used as an expression
In field test.str i have strings like 66:07:21 01 001 in uchastki.kadnum 66:07:21 01 001:27.
How to find substring using results of subquery?
UPDATE
Table test:
CREATE TABLE test
(
id serial NOT NULL,
str character varying(255)
)
WITH (
OIDS=FALSE
);
ALTER TABLE test OWNER TO postgres;
Table uchastki:
CREATE TABLE uchastki
(
fid serial NOT NULL,
the_geom geometry,
id_uch integer,
num_opora character varying,
kod_lep integer,
kadnum character varying,
sq real,
kod_type_opora character varying,
num_f11s integer,
num_opisanie character varying,
CONSTRAINT uchastki_pkey PRIMARY KEY (fid),
CONSTRAINT enforce_dims_the_geom CHECK (st_ndims(the_geom) = 2)
)
WITH (
OIDS=FALSE
);
ALTER TABLE uchastki OWNER TO postgres;
Use like any :
SELECT uchastki.kadnum
FROM uchastki
WHERE kadnum LIKE ANY(
SELECT str
FROM test
WHERE str IS NOT NULL)
Or perhaps:
SELECT uchastki.kadnum
FROM uchastki
WHERE kadnum LIKE ANY(
SELECT '%' || str || '%'
FROM test
WHERE str IS NOT NULL)
this is a nice feature, You can use different operators, for example = any (select ... ), or <> all (select...).
I'm going to take a wild stab in the dark and assume you mean that you want to match a string Sa from table A against one or more other strings S1 .. Sn from table B to find out if any of the other strings in S1 .. Sn is a substring of Sa.
A simple example to show what I mean (hint, hint):
Given:
CREATE TABLE tableA (string_a text);
INSERT INTO tableA(string_a) VALUES
('the manual is great'), ('Chicken chicken chicken'), ('bork');
CREATE TABLE tableB(candidate_str text);
INSERT INTO tableB(candidate_str) VALUES
('man'),('great'),('chicken');
I want the result set:
the manual is great
chicken chicken chicken
because the manual is great has man and great in it; and because chicken chicken chicken has chicken in it. There is no need to show the substring(s) that matched. bork doesn't match any substring so it is not found.
Here's a SQLFiddle with the sample data.
If so, shamelessly stealing #maniek's excellent suggestion, you would use:
SELECT string_a
FROM tableA
WHERE string_a LIKE ANY (SELECT '%'||candidate_str||'%' FROM tableB);
(Vote for #maniek please, I'm just illustrating how to clearly explain - I hope - what you want to achieve, sample data, etc).
(Note: This answer was written before further discussion clarified the poster's actual intentions)
It would appear highly likely that there is more than one str in test where str IS NOT NULL. That's why more than one row is returned by the subquery used as an expression, and, thus, why the statement fails.
Run the subquery stand-alone to see what it returns and you'll see. Perhaps you intended it to be a correlated subquery but forgot the outer column-reference? Or perhaps there's a column also called str in the outer table and you meant to write:
SELECT uchastki.kadnum
FROM uchastki
WHERE kadnum LIKE (
SELECT test.str
FROM test
WHERE uchastki.str IS NOT NULL)
?
(Hint: Consistently using table aliases on column references helps to avoid name-clash confusion).
Related
Assume I have the following table
CREATE TABLE my_table (
id UUID,
field_1 VARCHAR
field_2 VARCHAR
field_3 VARCHAR
);
This table is linked to a form in front-end where I can update field_1, field_2 and field_3, but I want to only update the fields that the user has filled.
For software and team reasons, we have the following function to update, built and called from front-end:
CREATE OR REPLACE FUNCTION public.update_my_table(
_my_table_input my_table%ROWTYPE
) RETURNS VOID AS $$
BEGIN
UPDATE
my_table
SET
field_1 = _my_table_input.field_1
field_2 = _my_table_input.field_2,
field_3 = _my_table_input.field_3
WHERE
id = _my_table.id
END;
_my_table_input represents a row of table with an id and a list of all fields that the user has modified.
In the code I'm treating, the issue I'm facing is that I'm passing a table row which can be incomplete, meaning that for instance _my_table_input.field_3 can be null, and in this case, I don't want to update field_3
I could change the way the front works, but I was thinking and I'm looking for an elegant to UPDATE my data of only defined fields of the function input, something like:
UPDATE
my_table
SET
field_1 = _my_table_input.field_1 IF EXISTS(_my_table_input.field_1)
field_2 = _my_table_input.field_2 IF EXISTS(_my_table_input.field_2),
field_3 = _my_table_input.field_3 IF EXISTS(_my_table_input.field_3)
WHERE
id = _my_table.id
I checked online, and on PostgreSQL docs, but I could not find what I want. So I thought maybe some of you could have a brilliant idea.
How about using "dynamic query" approach?
I'd like to share the idea, although I probably don't know the most graceful way to do so with Postgreql, but I think it may go like these:
with
-- combining each desired field in temp table
set_fields (field) as (
values
(case
when _my_table_input.field1 is not null
then concat('field1 = ', _my_table_input.field1)
else null
end),
(case
when _my_table_input.field2 is not null
then concat('field2 = ', _my_table_input.field2)
else null
end),
),
-- aggregating parts of statement in another temp table
update_sql_statement as (
select
concat(
'update ',
'my_table ',
'set ',
array_to_string(array_agg(field), ', ')
-- ' where id = _my_table.id'
) as statement
from set_fields
)
-- Then take the finally runable statment string out to execute
-- This is the part I'm not sure about, but I believe postgresql have some way of executing certain string from temp.
-- execute(format(select statement from update_sql_statement))
select statement from update_sql_statement
Try see this doc if it help spark something.
I have a table in postgres:
create table fubar (
name1 text,
name2 text, ...,
key integer);
I want to write a function which returns field values from fubar given the column names:
function getFubarValues(col_name text, key integer) returns text ...
where getFubarValues returns the value of the specified column in the row identified by key. Seems like this should be easy.
I'm at a loss. Can someone help? Thanks.
Klin's answer is a good (i.e. safe) approach to the question as posed, but it can be simplified:
PostgreSQL's -> operator allows expressions. For example:
CREATE TABLE test (
id SERIAL,
js JSON NOT NULL,
k TEXT NOT NULL
);
INSERT INTO test (js,k) VALUES ('{"abc":"def","ghi":"jkl"}','abc');
SELECT js->k AS value FROM test;
Produces
value
-------
"def"
So we can combine that with row_to_json:
CREATE TABLE test (
id SERIAL,
a TEXT,
b TEXT,
k TEXT NOT NULL
);
INSERT INTO test (a,b,k) VALUES
('foo','bar','a'),
('zip','zag','b');
SELECT row_to_json(test)->k AS value FROM test;
Produces:
value
-------
"foo"
"zag"
Here I'm getting the key from the table itself but of course you could get it from any source / expression. It's just a value. Also note that the result returned is a JSON value type (it doesn't know if it's text, numeric, or boolean). If you want it to be text, just cast it: (row_to_json(test)->k)::TEXT
Now that the question itself is answered, here's why you shouldn't do this, and what you should do instead!
Never trust any data. Even if it already lives inside your database, you shouldn't trust it. The method I've posted here is safe against SQL injection attacks, but an attacker could still set k to 'id' and see a column which was not intended to be visible to them.
A much better approach is to structure your data with this type of query in mind. Postgres has some excellent datatypes for this; HSTORE and JSON/JSONB. Merge your dynamic columns into a single column with one of those types (I'd suggest HSTORE for its simplicity and generally being more complete).
This has several advantages: your schema is well-defined and does not need to change if you add more dynamic columns, you do not need to perform expensive re-casting (i.e. row_to_json), and you are able to take advantage of indexes on your columns (thanks to PostgreSQL's functional indexes).
The equivalent to the code I wrote above would be:
CREATE EXTENSION HSTORE; -- necessary if you're not already using HSTORE
CREATE TABLE test (
id SERIAL,
cols HSTORE NOT NULL,
k TEXT NOT NULL
);
INSERT INTO test (cols,k) VALUES
('a=>"foo",b=>"bar"','a'),
('a=>"zip",b=>"zag"','b');
SELECT cols->k AS value FROM test;
Or, for automatic escaping of your values when inserting, you can use one of:
INSERT INTO test (cols,k) VALUES
(hstore( 'a', 'foo' ) || hstore( 'b', 'bar' ), 'a'),
(hstore( ARRAY['a','b'], ARRAY['zip','zag'] ), 'b');
See http://www.postgresql.org/docs/9.1/static/hstore.html for more details.
You can use dynamic SQL to select a column by name:
create or replace function get_fubar_values (col_name text, row_key integer)
returns setof text language plpgsql as $$begin
return query execute 'select ' || quote_ident(col_name) ||
' from fubar where key = $1' using row_key;
end$$;
The end result of what I am after is a query that calls a function and that function returns a set of records that are in their own separate fields. I can do this but the results of the function are all in one field.
ie: http://i.stack.imgur.com/ETLCL.png and the results I am after are: http://i.stack.imgur.com/wqRQ9.png
Here's the code to create the table
CREATE TABLE tbl_1_hm
(
tbl_1_hm_id bigserial NOT NULL,
tbl_1_hm_f1 VARCHAR (250),
tbl_1_hm_f2 INTEGER,
CONSTRAINT tbl_1_hm PRIMARY KEY (tbl_1_hm_id)
)
-- do that for a few times to get some data
INSERT INTO tbl_1_hm (tbl_1_hm_f1, tbl_1_hm_f2)
VALUES ('hello', 1);
CREATE OR REPLACE FUNCTION proc_1_hm(id BIGINT)
RETURNS TABLE(tbl_1_hm_f1 VARCHAR (250), tbl_1_hm_f2 int AS $$
SELECT tbl_1_hm_f1, tbl_1_hm_f2
FROM tbl_1_hm
WHERE tbl_1_hm_id = id
$$ LANGUAGE SQL;
--And here is the current query I am running for my results:
SELECT t1.tbl_1_hm_id, proc_1_hm(t1.tbl_1_hm_id) AS t3
FROM tbl_1_hm AS t1
Thanks for having a read. Please if you want to haggle about the semantics of what I am doing by hitting the same table twice or my naming convention --> this is a simplified test.
When a function returns a set of records, you should treat it as a table source:
SELECT t1.tbl_1_hm_id, t3.*
FROM tbl_1_hm AS t1, proc_1_hm(t1.tbl_1_hm_id) AS t3;
Note that functions are implicitly using a LATERAL join (scroll down to sub-sections 4 and 5) so you can use fields from tables listed previously without having to specify an explicit JOIN condition.
Execute the following scripts using TD14. When I run the final SELECT, HashedField returns F5-23-BA-34, but HashedConstant returns 2C-30-5B-4F.
Notice how, for the INSERT, I am qualifying the constant No as constant and then using hashrow() on that qualifier. When the hashed qualifier goes in, it does so as 2C-30-5B-4F as expected. However, when I try to hash the field itself in the select, I get F5-23-BA-34. This makes no sense to me.
create table mydb.mytable (
val VARCHAR(3) CHARACTER SET LATIN NOT CASESPECIFIC,
prehashedval byte(4)
);
insert into mydb.mytable
select 'No' constant, hashrow(constant);
select
hashrow(val) HashedField,
prehashedval,
hashrow('No') HashedConstant
from mydb.mytable;
It's not the same string :-)
A string literal is always in Unicode, which hashes differently from Latin:
CREATE VOLATILE TABLE mytable (
MyField VARCHAR(3) CHARACTER SET LATIN NOT CASESPECIFIC,
MyField_U VARCHAR(3) CHARACTER SET UNICODE NOT CASESPECIFIC
) ON COMMIT PRESERVE ROWS;
INSERT INTO mytable('No', 'No');
SELECT
HASHROW(MyField) HashedField,
HASHROW(TRANSLATE('No' USING unicode_to_latin)),
HASHROW(MyField_U) HashedField_U,
HASHROW('No')
FROM mytable;
What are good ways to add a constraint to PostgreSQL to check that exactly one column (from a set of columns) contains a non-null value?
Update: It is likely that I want to use a check expression as detailed in Create Table and Alter Table.
Update: I'm looking through the available functions.
Update: Just for background, here is the Rails validation logic I'm currently using:
validate :multi_column_validation
def multi_column_validation
n = 0
n += 1 if column_1
n += 1 if column_2
n += 1 if column_3
unless 1 == n
errors.add(:base, "Exactly one column from " +
"column_1, column_2, column_3 must be present")
end
end
To be clear, I'm looking for PSQL, not Ruby, here. I just wanted to show the logic I'm using since it is more compact than enumerating all "truth table" possibilities.
Since PostgreSQL 9.6 you have the num_nonnulls and num_nulls comparison functions that accept any number of VARIADIC arguments.
For example, this would make sure exactly one of the three columns is not null.
ALTER TABLE your_table
ADD CONSTRAINT chk_only_one_is_not_null CHECK (num_nonnulls(col1, col2, col3) = 1);
History & References
The PostgreSQL 9.6.0 Release Notes from 2016-09-29 say:
Add variadic functions num_nulls() and num_nonnulls() that count the number of their arguments that are null or non-null (Marko Tiikkaja)
On 2015-08-12, Marko Tiikkaja proposed this feature on the pgsql-hacker mailing list:
I'd like to suggest $SUBJECT for inclusion in Postgres 9.6. I'm sure everyone would've found it useful at some point in their lives, and the fact that it can't be properly implemented in any language other than C I think speaks for the fact that we as a project should provide it.
A quick and dirty proof of concept (patch attached):
=# select count_nulls(null::int, null::text, 17, 'bar');
count_nulls
-------------
2
(1 row)
Its natural habitat would be CHECK constraints, e.g:
CHECK (count_nulls(a,b,c) IN (0, 3))
Avid code historians can follow more discussion from that point. :)
2021-09-17 update: As of today, gerardnll provides a better answer (the best IMO):
"Since PostgreSQL 9.6 you have the num_nonnulls and num_nulls comparison functions that accept any number of VARIADIC arguments."
In order to help people find the cleanest solution, I recommend you upvote gerardnll's answer.
(FYI, I'm the same person who asked the original question.)
Here is my original answer from 2013
Here is an elegant two column solution according to the "constraint -- one or the other column not null" PostgreSQL message board:
ALTER TABLE my_table ADD CONSTRAINT my_constraint CHECK (
(column_1 IS NULL) != (column_2 IS NULL));
(But the above approach is not generalizable to three or more columns.)
If you have three or more columns, you can use the truth table approach illustrated by a_horse_with_no_name. However, I consider the following to be easier to maintain because you don't have to type out the logical combinations:
ALTER TABLE my_table
ADD CONSTRAINT my_constraint CHECK (
(CASE WHEN column_1 IS NULL THEN 0 ELSE 1 END) +
(CASE WHEN column_2 IS NULL THEN 0 ELSE 1 END) +
(CASE WHEN column_3 IS NULL THEN 0 ELSE 1 END) = 1;
To compact this, it would be useful to create a custom function so that the CASE WHEN column_k IS NULL THEN 0 ELSE 1 END boilerplate could be removed, leaving something like:
(non_null_count(column_1) +
non_null_count(column_2) +
non_null_count(column_3)) = 1
That may be as compact as PSQL will allow (?). That said, I'd prefer to get to this kind of syntax if possible:
non_null_count(column_1, column_2, column_3) = 1
I think the most clean and generic solution is to create a function to count the null values from some arguments. For that you can use the pseudo-type anyarray and a SQL function like that:
CREATE FUNCTION count_not_nulls(p_array anyarray)
RETURNS BIGINT AS
$$
SELECT count(x) FROM unnest($1) AS x
$$ LANGUAGE SQL IMMUTABLE;
With that function, you can create your CHECK CONSTRAINT as:
ALTER TABLE your_table
ADD chk_only_one_is_not_null CHECK(count_not_nulls(array[col1, col2, col3]) = 1);
This will only work if the columns are of the same data type. If it's not the case, you can cast them, as text for instance (as you just care for the null case):
ALTER TABLE your_table
ADD chk_only_one_is_not_null CHECK(count_not_nulls(array[col1::text, col2::text, col3::text]) = 1);
As well remembered by #muistooshort, you can create the function with variadic arguments, which makes it clear to call:
CREATE FUNCTION count_not_nulls(variadic p_array anyarray)
RETURNS BIGINT AS
$$
SELECT count(x) FROM unnest($1) AS x
$$ LANGUAGE SQL IMMUTABLE;
ALTER TABLE your_table
ADD chk_only_one_is_not_null CHECK(count_not_nulls(col1, col2, col3) = 1);
As hinted by mu is too short:
alter table t
add constraint only_one_null check (
(col1 is not null)::integer + (col2 is not null)::integer = 1
)
A bit clumsy, but should do the trick:
create table foo
(
col1 integer,
col2 integer,
col3 integer,
constraint one_is_not_null check
( (col1 is not null and col2 is null and col3 is null)
or (col1 is null and col2 is not null and col3 is null)
or (col1 is null and col2 is null and col3 is not null)
)
)
Here's a solution using the built-in array functions:
ALTER TABLE your_table
ADD chk_only_one_is_not_null CHECK (array_length(array_remove(ARRAY[col1, col2, col3], NULL), 1) = 1);