I have a roles field in a Users table of type Array. I need to be able to get all users that have both role 'admin' and role 'member'. FOr now I use this query:
select * from users where 'member' = ANY(roles) and 'admin' = ANY(roles);
I was wondering if there was a cleaner way to do this.
Use the array-contained-by operator #<:
select * from users where ARRAY['member','admin']::varchar[] <# roles;
That'll let you index the lookup too.
(Correction per #bereal; I misread the question)
Or, if you meant that they must have both rights, use array-overlaps &&:
select * from users where ARRAY['member','admin']::varchar[] && roles;
Also, as your input turns out to be varchar[] (you didn't show your table definition), you must cast the array input to varchar[] too, as there's no implicit cast between array types.
Related
I have a column(user_array) in my Redshift DB table "User" with the data type Super which contains an array. It looks something like this.
["{\"username\":\"John\"","\"usertype\":\"ABC\"}"]
How do I query this table to pull the username?
I tried the following methods:
select x.username from User u
left join u.user_array x on true;
or
select u.user_array.username from User u;
Both statements return null.
What is the correct way of querying? I could not find it in any of the documentation.
I'm using a PostgreSQL with a Go driver. Sometimes I need to query not existing fields, just to check - maybe something exists in a DB. Before querying I can't tell whether that field exists. Example:
where size=10 or length=10
By default I get an error column "length" does not exist, however, the size column could exist and I could get some results.
Is it possible to handle such cases to return what is possible?
EDIT:
Yes, I could get all the existing columns first. But the initial queries can be rather complex and not created by me directly, I can only modify them.
That means the query can be simple like the previous example and can be much more complex like this:
WHERE size=10 OR (length=10 AND n='example') OR (c BETWEEN 1 and 5 AND p='Mars')
If missing columns are length and c - does that mean I have to parse the SQL, split it by OR (or other operators), check every part of the query, then remove any part with missing columns - and in the end to generate a new SQL query?
Any easier way?
I would try to check within information schema first
"select column_name from INFORMATION_SCHEMA.COLUMNS where table_name ='table_name';"
And then based on result do query
Why don't you get a list of columns that are in the table first? Like this
select column_name
from information_schema.columns
where table_name = 'table_name' and (column_name = 'size' or column_name = 'length');
The result will be the columns that exist.
There is no way to do what you want, except for constructing an SQL string from the list of available columns, which can be got by querying information_schema.columns.
SQL statements are parsed before they are executed, and there is no conditional compilation or no short-circuiting, so you get an error if a non-existing column is referenced.
Where data is JSONB column, I want to check if the key exists, normally I use:
SELECT id FROM users WHERE length(data->>'fingerprint_id') IS NULL;
Is there any better/shorter way to do this? since other alternative give incorrect result:
> SELECT id FROM users WHERE data->>'fingerprint_id' IS NULL;
ERROR: operator does not exist: jsonb ->> boolean
LINE 1: SELECT id FROM users WHERE data->>'fingerprint_id' IS NULL;
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
> SELECT id FROM users WHERE data->>'fingerprint_id' = '';
id
----
(0 rows)
There is an explicit operator (?) for this purpose:
where data_jsonb ? 'key'
But be aware, that this might cause some DB abstraction layers (f.ex. JDBC) to falsely recognize ? as an input parameter's placeholder.
As a workaround, you could use the jsonb_exists(json, text) function directly (but your code will then depend on an undocumented function), or define a similar operator for this, like in this answer.
More details about the (data ->> 'key') IS NULL syntax can be found here.
Apparently, I just need to enclose the query with () before IS NULL
SELECT id FROM users WHERE (data->>'fingerprint_id') IS NULL;
Not sure whether this is possible and what's the best way to do it, but I have a list of values (say it is 'ACBC', 'ADFC', 'AGGD' etc.) which grows over time. I want to use these values in pgADmin as a sort of variable/parameter for SQL statements; e.g:
Codes = {'ACBC', 'ADFC', 'AGGD'}
SQL: Statement => SELECT * FROM xxx WHERE SUBSTRING IN (Codes)
Is that somehow realizable, either with a variable, parameter, function or anyhing else?
I can think of the following options:
1) Create separate table
create table qry_params(prm_value varchar);
insert into qry_params values
('xxx'),
('yyy'),
('zzz');
select * from xxx where substring in (select prm_value from qry_params)
Whenever you have new parameter, you just need to add it to the table.
2) CTE at the top of query
Use query like:
with params (prm_value) as (select values ('xxx'), ('yyy'), ('zzz'))
select * from xxx where substring in (select prm_value from qry_params)
Whenever you have new parameter, you just need to add it to the CTE.
Is there any way to write case-insensitive queries in PostgreSQL, E.g. I want that following 3 queries return same result.
SELECT id FROM groups where name='administrator'
SELECT id FROM groups where name='ADMINISTRATOR'
SELECT id FROM groups where name='Administrator'
Use LOWER function to convert the strings to lower case before comparing.
Try this:
SELECT id
FROM groups
WHERE LOWER(name)=LOWER('Administrator')
using ILIKE instead of LIKE
SELECT id FROM groups WHERE name ILIKE 'Administrator'
The most common approach is to either lowercase or uppercase the search string and the data. But there are two problems with that.
It works in English, but not in all languages. (Maybe not even in
most languages.) Not every lowercase letter has a corresponding
uppercase letter; not every uppercase letter has a corresponding
lowercase letter.
Using functions like lower() and upper() will give you a sequential
scan. It can't use indexes. On my test system, using lower() takes
about 2000 times longer than a query that can use an index. (Test data has a little over 100k rows.)
There are at least three less frequently used solutions that might be more effective.
Use the citext module, which mostly mimics the behavior of a case-insensitive data type. Having loaded that module, you can create a case-insensitive index by CREATE INDEX ON groups (name::citext);. (But see below.)
Use a case-insensitive collation. This is set when you initialize a
database. Using a case-insensitive collation means you can accept
just about any format from client code, and you'll still return
useful results. (It also means you can't do case-sensitive queries. Duh.)
Create a functional index. Create a lowercase index by using CREATE
INDEX ON groups (LOWER(name));. Having done that, you can take advantage
of the index with queries like SELECT id FROM groups WHERE LOWER(name) = LOWER('ADMINISTRATOR');, or SELECT id FROM groups WHERE LOWER(name) = 'administrator'; You have to remember to use LOWER(), though.
The citext module doesn't provide a true case-insensitive data type. Instead, it behaves as if each string were lowercased. That is, it behaves as if you had called lower() on each string, as in number 3 above. The advantage is that programmers don't have to remember to lowercase strings. But you need to read the sections "String Comparison Behavior" and "Limitations" in the docs before you decide to use citext.
You can use ILIKE. i.e.
SELECT id FROM groups where name ILIKE 'administrator'
You can also read up on the ILIKE keyword. It can be quite useful at times, albeit it does not conform to the SQL standard. See here for more information: http://www.postgresql.org/docs/9.2/static/functions-matching.html
You could also use POSIX regular expressions, like
SELECT id FROM groups where name ~* 'administrator'
SELECT 'asd' ~* 'AsD' returns t
use ILIKE
select id from groups where name ILIKE 'adminstration';
If your coming the expressjs background and name is a variable
use
select id from groups where name ILIKE $1;
Using ~* can improve greatly on performance, with functionality of INSTR.
SELECT id FROM groups WHERE name ~* 'adm'
return rows with name that contains OR equals to 'adm'.
ILIKE work in this case:
SELECT id
FROM groups
WHERE name ILIKE 'Administrator'
For a case-insensitive parameterized query, you can use the following syntax:
"select * from article where upper(content) LIKE upper('%' || $1 || '%')"
-- Install 'Case Ignore Test Extension'
create extension citext;
-- Make a request
select 'Thomas'::citext in ('thomas', 'tiago');
select name from users where name::citext in ('thomas', 'tiago');
If you want not only upper/lower case but also diacritics, you can implement your own func:
CREATE EXTENSION unaccent;
CREATE OR REPLACE FUNCTION lower_unaccent(input text)
RETURNS text
LANGUAGE plpgsql
AS $function$
BEGIN
return lower(unaccent(input));
END;
$function$;
Call is then
select lower_unaccent('Hôtel')
>> 'hotel'
A tested approach is using ~*
As in the example below
SELECT id FROM groups WHERE name ~* 'administrator'
select id from groups where name in ('administrator', 'ADMINISTRATOR', 'Administrator')