PostgreSQL trigger execute procedure or join on NEW - postgresql
-- trigger function
CREATE OR REPLACE FUNCTION fn_cities_geo_update_event() RETURNS trigger AS $fn_cities_geo_update_event$
BEGIN
is it ok to set a value (geog) which will be used later in the function?
NEW.geog := ST_SetSRID(ST_MakePoint(NEW.longitude,NEW.latitude), 4326)::geography;
this is one way that I tried to find the average of all cities within 90km of a new city so that the new city will have data populated about it
if NEW.rent_avg IS null then
NEW.rent_avg = (
SELECT avg(a.rent_avg)
FROM cities as a
-- I think I'm missing something here... ?
ST_DWithin(a.geog, NEW.geog, 90000)
);
end if;
here is another way that I tried:
if NEW.food_avg IS null then
NEW.food_avg := (
SELECT avg(a.food_avg)
FROM cities AS a
JOIN NEW AS b
ON ST_DWithin(a.geog, b.geog, 90000)
);
end if;
RETURN NEW;
END;
$fn_cities_geo_update_event$ LANGUAGE plpgsql;
but neither worked.
edit: here is a copy of the table that I'm working with
l |u |n |population|unesco|r |c |rent_avg|rent_low|rent_high|food_avg|food_low|food_high|transport_avg|transport_low|transport_high|k |i |quality|hcid |hc |latitude |longitude |spread |density |distance |dbn |state|geog |id
-----|--|--------------------------|----------|------|-------------------------|----------------|--------|--------|---------|--------|--------|---------|-------------|-------------|--------------|---------|---|-------|-------------------------------------|----|-----------|----------|-----------|----------|----------|--------------------------|-----|--------------------------------------------------|-----
false|NZ|Gisborne | 34274| 0|Australia and New Zealand|New Zealand | 92.2430| 51.1720| 143.4150| 22.0300| 13.3190| 35.3490| 7.0650| 5.9800| 13.0450|4VHV8X00+|GIS| 1712|place:Gisborne | 46|-38.6640015|177.977005| 0.99940002| 0| | | |0101000020E6100000000000A0433F664000000000FE5443C0| 1611
true |NZ|Patutahi | 386| |Australia and New Zealand|New Zealand | | | | | | | | | |4VHV9V00+| | 1000|place:Patutahi | 35|-38.6170006|177.899994| | | 8.5|Patutahi | |0101000020E6100000000000C0CC3C6640000000E0F94E43C0| 1624
true |NZ|Waihau Bay | | |Australia and New Zealand|New Zealand | | | | | | | | | |4VJV8Q00+| | 1000|place:Waihau_Bay | 6|-37.6780014|177.796005| | |110.699997|Waihau Bay | |0101000020E6100000000000E078396640000000C0C8D642C0| 1671
true |NZ|Tokomaru Bay | 495| |Australia and New Zealand|New Zealand | | | | | | | | | |4VHWV800+| | 1000|place:Tokomaru_Bay | 5|-38.1329994|178.300003| | |65.4000015|Tokomaru Bay | |0101000020E6100000000000A09949664000000020061143C0| 1673
true |FR|Cornebarrieu | | |Western Europe |France | | | | | | | | | |8FM3M800+| | 1000|place:Cornebarrieu | 112| 43.6559982|1.33299994| 3.60581994| | 3.5999999|Cornebarrieu | |0101000020E6100000000000C0F753F53F000000C0F7D34540| 6070
edit: create trigger statement
DROP TRIGGER IF EXISTS tr_cities_inserted ON cities;
CREATE TRIGGER tr_cities_inserted
BEFORE INSERT ON cities
FOR EACH ROW
EXECUTE PROCEDURE fn_cities_geo_update_event();
Okay, this is interesting.. I am new to triggers (as you may have noticed ^^) so it seems, when I recreate a table AFTER I created a trigger for that table, the trigger doesn't work. It just returns null for the respective columns.
When I stick to the proper order, I get it to work with my sample reproduction of your case:
CREATE TABLE so_postgres
(n text,
r text,
c text,
rent_avg numeric,
food_avg numeric,
transport_avg numeric,
latitude numeric,
longitude numeric,
geog geography
);
INSERT INTO so_postgres
VALUES ('Möhringen', 'central europe', 'germany', 200.45, 56.45, 4.56, 48.725866,
9.146131, ST_SetSRID(ST_Point(9.146131, 48.725866), 4326)),
('Vaihingen', 'central europe', 'germany', 155.33, 44.12, 2.78, 48.732550,
9.108291, ST_SetSRID(ST_Point(9.108291, 48.732550), 4326)),
('Sigmaringen', 'central europe', 'germany', 298.11, 59.67, 1.99, 48.090797,
9.230243, ST_SetSRID(ST_Point(9.230243, 48.090797), 4326));
CREATE OR REPLACE FUNCTION fn_cities_geo_update_event() RETURNS trigger AS $fn_cities_geo_update_event$
BEGIN
NEW.geog := (ST_SetSRID(ST_MakePoint(NEW.longitude,NEW.latitude), 4326)::geography);
if NEW.rent_avg IS null then
NEW.rent_avg := (
SELECT round(avg(a.rent_avg), 2)
FROM so_postgres as a
WHERE ST_DWithin(a.geog, NEW.geog, 50000)
);
end if;
RETURN NEW;
END;
$fn_cities_geo_update_event$ LANGUAGE plpgsql;
CREATE TRIGGER fn_cities_geo_update_event BEFORE INSERT OR UPDATE ON so_postgres FOR EACH ROW EXECUTE PROCEDURE fn_cities_geo_update_event();
INSERT INTO so_postgres (n, r, c, latitude, longitude)
VALUES ('Degerloch', 'central europe', 'germany', 48.725866, 9.146131);
n | r | c | rent_avg | food_avg | transport_avg | latitude | longitude | geog
-------------+----------------+---------+----------+----------+---------------+-----------+-----------+----------------------------------------------------
Möhringen | central europe | germany | 200.45 | 56.45 | 4.56 | 48.725866 | 9.146131 | 0101000020E610000012DDB3AED14A2240A1A3552DE95C4840
Vaihingen | central europe | germany | 155.33 | 44.12 | 2.78 | 48.732550 | 9.108291 | 0101000020E6100000FBE6FEEA71372240A857CA32C45D4840
Sigmaringen | central europe | germany | 298.11 | 59.67 | 1.99 | 48.090797 | 9.230243 | 0101000020E61000000F441669E275224097C9703C9F0B4840
Degerloch | central europe | germany | 177.89 | | | 48.725866 | 9.146131 | 0101000020E610000012DDB3AED14A2240A1A3552DE95C4840
To answer your last comment: I add the ST_DWithin to the rest of the query via a WHERE-clause.
Does it work for you?
Related
Interpretation of rows in Phoenix SYSTEM.CATALOG
When I create a Phoenix table there are two extra rows in SYSTEM.CATALOG. These are the first and the second rows in the output of SELECT * FROM SYSTEM.CATALOG ...... below. Can someone please help me understand what these two rows signify? The third and fourth rows in the output of SELECT * FROM SYSTEM.CATALOG ...... below are easily relatable to the CREATE TABLE statement. Therefore, they look fine. 0: jdbc:phoenix:t40aw2.gaq> CREATE TABLE C5 (company_id INTEGER PRIMARY KEY, name VARCHAR(225)); No rows affected (4.618 seconds) 0: jdbc:phoenix:t40aw2.gaq> select * from C5; +-------------+-------+ | COMPANY_ID | NAME | +-------------+-------+ +-------------+-------+ No rows selected (0.085 seconds) 0: jdbc:phoenix:t40aw2.gaq> SELECT * FROM SYSTEM.CATALOG WHERE TABLE_NAME='C5'; +------------+--------------+-------------+--------------+----------------+----------------+-------------+----------+---------------+---------------+------------------+--------------+----+ | TENANT_ID | TABLE_SCHEM | TABLE_NAME | COLUMN_NAME | COLUMN_FAMILY | TABLE_SEQ_NUM | TABLE_TYPE | PK_NAME | COLUMN_COUNT | SALT_BUCKETS | DATA_TABLE_NAME | INDEX_STATE | IM | +------------+--------------+-------------+--------------+----------------+----------------+-------------+----------+---------------+---------------+------------------+--------------+----+ | | | C5 | | | 0 | u | | 2 | null | | | fa | | | | C5 | | 0 | null | | | null | null | | | | | | | C5 | COMPANY_ID | | null | | | null | null | | | | | | | C5 | NAME | 0 | null | | | null | null | | | | +------------+--------------+-------------+--------------+----------------+----------------+-------------+----------+---------------+---------------+------------------+--------------+----+ 4 rows selected (0.557 seconds) 0: jdbc:phoenix:t40aw2.gaq> The Phoenix version I am using is: 4.1.8.29 Kindly note that no other operations where done on the table other than the 3 listed above, namely, create table, select * from the table, and select * from system.catalog where TABLE_NAME=the concerned table name.
Why can't I use a plsql argument in this where clause?
I have a function below (is_organizer) that works, and lets me use this method as a computed field in Hasura. The function below (is_chapter_member) which is almost identical, doesn't work. WORKS CREATE OR REPLACE FUNCTION is_organizer(event_row events, hasura_session json) RETURNS boolean AS $$ SELECT EXISTS ( SELECT 1 FROM event_organizers o WHERE o.user_id::text = hasura_session->>'x-hasura-user-id' AND (event_row.id = o.event_id OR event_row.event_template_id = o.event_template_id) ); $$ LANGUAGE SQL STRICT IMMUTABLE; BROKEN CREATE OR REPLACE FUNCTION is_chapter_member(c chapters, hasura_session json) RETURNS boolean AS $$ SELECT EXISTS ( SELECT 1 FROM chapter_members m WHERE m.user_id::text = hasura_session->>'x-hasura-user-id' AND c.chapter_id = m.chapter_id ); $$ LANGUAGE SQL STRICT IMMUTABLE; When attempting to add this function (not call it, just create it) Postgres gives me the following error: ERROR: missing FROM-clause entry for table "c" LINE 9: c.chapter_id = m.chapter_id Why would a function param need a where clause? Table dumps below... Table "public.chapters" Column | Type | Collation | Nullable | Default -----------------+--------------------------+-----------+----------+-------------------------------------- id | integer | | not null | nextval('chapters_id_seq'::regclass) title | text | | not null | slug | text | | not null | description | jsonb | | | avatar_url | text | | | photo_url | text | | | region | text | | | maps_api_result | jsonb | | | lat | numeric(11,8) | | | lng | numeric(11,8) | | | created_at | timestamp with time zone | | not null | now() updated_at | timestamp with time zone | | not null | now() deleted_at | timestamp with time zone | | | Table "public.chapter_members" Column | Type | Collation | Nullable | Default ------------+--------------------------+-----------+----------+--------- user_id | integer | | not null | chapter_id | integer | | not null | created_at | timestamp with time zone | | not null | now() updated_at | timestamp with time zone | | not null | now() Table "public.events" Column | Type | Collation | Nullable | Default -------------------+-----------------------------+-----------+----------+--------------------------------------------------- id | integer | | not null | nextval('events_id_seq'::regclass) event_template_id | integer | | not null | venue_id | integer | | | starts_at | timestamp without time zone | | not null | duration | interval | | not null | title | text | | | slug | text | | | description | text | | | photo_url | text | | | created_at | timestamp without time zone | | not null | now() updated_at | timestamp without time zone | | not null | now() deleted_at | timestamp without time zone | | | ends_at | timestamp without time zone | | | generated always as (starts_at + duration) stored Table "public.event_organizers" Column | Type | Collation | Nullable | Default -------------------+---------+-----------+----------+---------------------------------------------- id | integer | | not null | nextval('event_organizers_id_seq'::regclass) user_id | integer | | not null | event_id | integer | | | event_template_id | integer | | |
This turned out to be using an incorrect column name in the broken function. chapter_id should have just been id on the c argument. I took Richard's prompt and tried putting parens around the arg like (c).chapter_id. This then correctly told me that chapter_id doesn't exist, and allowed me to fix the issue.
how to retrieve information from three tables in below conditions in postgresql
I have three tables. TABLE_1: T2_ID ver date boolean --------------------------------------------------------- 1 | X-20-50 | 2019-01-01 16:20:51.722336+00 | TRUE 2 | X-50-30 | 2019-02-26 16:20:51.722336+00 | TRUE 3 | X-20-32 | 2019-03-20 16:20:51.722336+00 | FALSE 1 | X-20-50 | 2019-01-09 16:20:51.722336+00 | FALSE 2 | X-20-50 | 2019-12-02 16:20:51.722336+00 | TRUE 3 | X-20-50 | 2019-01-24 16:20:51.722336+00 | TRUE TABLE_2: id | type | scheduler -------------------------------------------------- 1 | ABC | w1,w2,w3,w4,w5,w6,w7,w8,w9,w10,w11,w12 2 | PQR | w5,w9 3 | TRC | w1,w4,w8 TABLE_3 start_date_of_ver | end_date_of_ver | ver_name ----------------------------------------------------------- 2019-01-01 00:00:00+00 | 2019-04-01 00:00:00+00 | X-20-50 2019-02-25 00:00:00+00 | 2019-05-26 00:00:00+00 | X-50-30 2019-03-15 00:00:00+00 | 2019-06-06 00:00:00+00 | X-20-32 Table 4 should fulfill the below condition. it takes version name (ver_name) as input from this (ver_name), it takes start date and end date of version (from table_3) if the version period is 3 months then it creates 12 weeks table with id (type) as the first column and creates an entry of twelve-week according to table 2 of the scheduler. information on table 4 will be updated as and when table 1 has entries of that particular week which are TRUE Note: table 1, entries get generates on a daily basis. Desired table: which has only ver_name as input and calculate below table. When table_1 don't have any entries then table_4 should look like as below Table_4: X-20-50 id_of_table_2 | week_1 | week_2 | week_3 | week_4 | week_5 | week_6 | week_7 | week_8 | week_9 | week_10 | week_11 | week_12 | ------------------------------------------------------------------------------------------------------------------------------ ABC | w1 | w2 | w3 | w4 | w5 | w6 | w7 | w8 | w9 | w10 | w11 | w12 | PQR | | | | | w5 | | | | w9 | | | | TRC | w1 | | | w4 | | | | w8 | | | | | When table_1 has entries then table_4 should look like as below X-20-50 id_of_table_2 | week_1 | week_2 | week_3 | week_4 | week_5 | week_6 | week_7 | week_8 | week_9 | week_10 | week_11 | week_12 | ------------------------------------------------------------------------------------------------------------------------------ ABC | Done | Done | w3 | w4 | w5 | w6 | w7 | w8 | w9 | w10 | w11 | w12 | PQR | | | | | w5 | | | | w9 | | | | TRC | Done | | | w4 | | | | w8 | | | | |
You can create function which can take starting date of a week as input. Example- create function a(start_date) RETURNS json LANGUAGE 'plpgsql' COST 100 VOLATILE AS $BODY$ DECLARE outputjson json; BEGIN EXECUTE 'select json_agg(*) from table_name where date >= '||start_date||' and (date '||start_date||' + integer ''7'')' into outputjson; RETURN outputjson; END; $$ Hope this will help.
Your requirement needs a little refinement. You specify to retrieve weekly data yet fail to define a your week. On what day does it begin? Are all weeks 7 days long? What happens when Dec 31 falls on Tuesday is Friday Jan 3 in the same week (see current year calendar). Then there is the issue of user input and what it represents. Is it the desired start date and the week is that date and the next 6 days or any date within weekly period? The following assumes an ISO 8601 definition (google it - lots of stuff). Every week begins on Monday and all weeks are 7 days long. (Thus the week containing 31-Dec-2019 also includes 3-Jan-2020). The routine extracts the ISO Year and ISO week user entered date. --setup create table weekly_something( c1 text, c2 text, date1 timestamptz, someem boolean); insert into weekly_something( c1, c2, date1, someem ) values ('ABC','AB-20-50','2019-11-25 16:20:51.722336+00',TRUE) , ('PQR','AB-50-30','2019-11-26 16:20:51.722336+00',TRUE) , ('TRC','CD-20-32','2019-11-27 16:20:51.722336+00',FALSE) , ('ABC','AB-20-50','2019-12-02 16:20:51.722336+00',FALSE) , ('ABC','AB-20-50','2019-12-02 16:20:51.722336+00',TRUE) , ('JFF','yy-45-89','2019-12-31 16:20:51.722336+00',TRUE) , ('JFF','yy-89-30','2020-01-03 16:20:51.722336+00',TRUE) ; -- JFF Just For Fun -- SQL Function create function week_of(week_date date) returns setof weekly_something language sql stable strict as $$ select * from weekly_something where (extract('isoyear' from week_date), extract('week' from week_date)) = (extract('isoyear' from date1), extract('week' from date1)); $$; -- test select * from week_of('2019-11-26'); select * from week_of('2019-12-30');
PostgreSQL crosstab doesn't work as desired
In this example, I expect the resulting pivot table to have values for 4 columns, but instead there's only values for 2. It should've returned something like this: | time | trace1 | trace2 | trace3 | trace4 | | -----------------------------------------| | t | v | v | v | v | | t | v | v | v | null | | t | null | v | v | v | | t | v | v | null | v | | t | v | null | v | v | |------------------------------------------| but I got this instead: | time | trace1 | trace2 | trace3 | trace4 | | -----------------------------------------| | t | v | v | null | null | | t | v | v | null | null | | t | v | v | null | null | | t | v | null | null | null | | t | v | null | null | null | |------------------------------------------| Even worse, if I remove order by unixdatetime , everything will be smashed into only 1 column as below: | time | trace1 | trace2 | trace3 | trace4 | | -----------------------------------------| | t | v | null | null | null | | t | v | null | null | null | | t | v | null | null | null | | t | v | null | null | null | | t | v | null | null | null | |------------------------------------------| Here's the code: select * from crosstab( $$ select unixdatetime, gaugesummaryid, value::double precision from (values (1546300800,187923,1.5), (1546387200,187923,1.5), (1546473600,187923,1.5), (1546560000,187923,1.75), (1546646400,187923,1.75), (1546732800,187923,1.75), (1546819200,187923,1.75), (1546905600,187923,1.5), (1546992000,187923,1.5), (1547078400,187923,1.5), (1547164800,187923,1.5), (1547337600,187924,200), (1547424000,187924,200), (1547510400,187924,200), (1547596800,187924,200), (1547683200,187924,200), (1547769600,187924,200), (1547856000,187924,200), (1547942400,187924,200), (1548028800,187924,200), (1548115200,187924,200), (1548201600,187924,200), (1548288000,187924,200), (1546300800,187926,120), (1546387200,187926,120), (1546473600,187926,120), (1546560000,187926,110), (1546646400,187926,110), (1546732800,187926,110), (1546819200,187926,110), (1546905600,187926,115), (1546992000,187926,115), (1547078400,187926,115), (1547942400,187927,100), (1548028800,187927,100), (1548115200,187927,100), (1548201600,187927,100), (1548288000,187927,100) ) as t (unixdatetime, gaugesummaryid, value) order by unixdatetime $$ ) as final_result ( unixdatetime int, trace1 double precision, trace2 double precision, trace3 double precision, trace4 double precision ); Here's the link in case you'd like to play around: https://dbfiddle.uk/?rdbms=postgres_11&fiddle=2c4f6098fb89b78898ba1bf6afa7f439 How to get the desired result?
I would recommend you to use filter (where ...) clause instead of a pivot table. select unixdatetime, min(value) filter (where gaugesummaryid = 187923) as trace_1, min(value) filter (where gaugesummaryid = 187924) as trace_2, min(value) filter (where gaugesummaryid = 187926) as trace_3, min(value) filter (where gaugesummaryid = 187927) as trace_4 from table group by 1; Note, that you have to use an aggregate function to be able to use the clause. In your case, it does not matter if you use min, max, avg or sum.
Use the 2-argument form of the crosstab function: SELECT * FROM crosstab( $$ SELECT unixdatetime, gaugesummaryid, value::double precision FROM test ORDER BY unixdatetime $$ , 'SELECT DISTINCT gaugesummaryid FROM test ORDER BY 1 LIMIT 4' ) as final_result ( unixdatetime int, trace1 double precision, trace2 double precision, trace3 double precision, trace4 double precision ) yields | unixdatetime | trace1 | trace2 | trace3 | trace4 | |--------------+--------+--------+--------+--------| | 1546300800 | 1.5 | | 120 | | | 1546387200 | 1.5 | | 120 | | | 1546473600 | 1.5 | | 120 | | | 1546560000 | 1.75 | | 110 | | | 1546646400 | 1.75 | | 110 | | | 1546732800 | 1.75 | | 110 | | | 1546819200 | 1.75 | | 110 | | | 1546905600 | 1.5 | | 115 | | | 1546992000 | 1.5 | | 115 | | | 1547078400 | 1.5 | | 115 | | | 1547164800 | 1.5 | | | | | 1547337600 | | 200 | | | | 1547424000 | | 200 | | | | 1547510400 | | 200 | | | | 1547596800 | | 200 | | | | 1547683200 | | 200 | | | | 1547769600 | | 200 | | | | 1547856000 | | 200 | | | | 1547942400 | | 200 | | 100 | | 1548028800 | | 200 | | 100 | | 1548115200 | | 200 | | 100 | | 1548201600 | | 200 | | 100 | | 1548288000 | | 200 | | 100 | Using this setup: DROP TABLE IF EXISTS test; CREATE TABLE test ( unixdatetime bigint, gaugesummaryid int, value double precision ); INSERT INTO test VALUES (1546300800,187923,1.5), (1546387200,187923,1.5), (1546473600,187923,1.5), (1546560000,187923,1.75), (1546646400,187923,1.75), (1546732800,187923,1.75), (1546819200,187923,1.75), (1546905600,187923,1.5), (1546992000,187923,1.5), (1547078400,187923,1.5), (1547164800,187923,1.5), (1547337600,187924,200), (1547424000,187924,200), (1547510400,187924,200), (1547596800,187924,200), (1547683200,187924,200), (1547769600,187924,200), (1547856000,187924,200), (1547942400,187924,200), (1548028800,187924,200), (1548115200,187924,200), (1548201600,187924,200), (1548288000,187924,200), (1546300800,187926,120), (1546387200,187926,120), (1546473600,187926,120), (1546560000,187926,110), (1546646400,187926,110), (1546732800,187926,110), (1546819200,187926,110), (1546905600,187926,115), (1546992000,187926,115), (1547078400,187926,115), (1547942400,187927,100), (1548028800,187927,100), (1548115200,187927,100), (1548201600,187927,100), (1548288000,187927,100);
While some of the target values may be missing , you need the 2-argument form of crosstab() (like unutbu provided). But it makes no sense to use a query producing unstable results as 2nd parameter. Use a VALUES expression (or similar) to provide a stable set of target columns in sync with the resulting column definition list. Like: SELECT * FROM crosstab( $$ SELECT * FROM ( VALUES (bigint '1546300800', 187923, float8 '1.5') , (1546387200,187923,1.5) , (1546473600,187923,1.5) -- , ... , (1548288000,187927,100) ) t (unixdatetime, gaugesummaryid, value) ORDER BY 1,2 $$ , 'VALUES (187923), (187924), (187926), (187927)' -- !! ) final_result (unixdatetime int , trace1 float8 , trace2 float8 , trace3 float8 , trace4 float8); db<>fiddle here Detailed explanation: PostgreSQL Crosstab Query It would be nice to get results for a dynamic number of target columns from a single query. Alas, SQL does not work like that. There are various workarounds. See: Execute a dynamic crosstab query
Left outer join - how to return a boolean for existence in the second table?
In PostgreSQL 9 on CentOS 6 there are 60000 records in pref_users table: # \d pref_users Table "public.pref_users" Column | Type | Modifiers ------------+-----------------------------+-------------------- id | character varying(32) | not null first_name | character varying(64) | not null last_name | character varying(64) | login | timestamp without time zone | default now() last_ip | inet | (... more columns skipped...) And another table holds around 500 ids of users which are not allowed to play anymore: # \d pref_ban2 Table "public.pref_ban2" Column | Type | Modifiers ------------+-----------------------------+--------------- id | character varying(32) | not null first_name | character varying(64) | last_name | character varying(64) | city | character varying(64) | last_ip | inet | reason | character varying(128) | created | timestamp without time zone | default now() Indexes: "pref_ban2_pkey" PRIMARY KEY, btree (id) In a PHP script I am trying to display all 60000 users from pref_users in a jQuery-dataTable. And I would like to mark the banned users (the users found in pref_ban2). Which means I need a column named ban for each record in my query holding true or false. So I am trying a left outer join query: # select b.id, -- how to make this column a boolean? u.id, u.first_name, u.last_name, u.city, u.last_ip, to_char(u.login, 'DD.MM.YYYY') as day from pref_users u left outer join pref_ban2 b on u.id=b.id limit 10; id | id | first_name | last_name | city | last_ip | day ----+----------+-------------+-----------+-------------+-----------------+------------ | DE1 | Alex | | Bochum | 2.206.0.224 | 21.11.2014 | DE100032 | Княжна Мэри | | London | 151.50.61.131 | 01.02.2014 | DE10011 | Aлександр Ш | | Симферополь | 37.57.108.13 | 01.01.2014 | DE10016 | Semen10 | | usa | 69.123.171.15 | 25.06.2014 | DE10018 | Горловка | | Горловка | 178.216.97.214 | 25.09.2011 | DE10019 | -Дмитрий- | | пермь | 5.140.81.95 | 21.11.2014 | DE10047 | Василий | | Cумы | 95.132.42.185 | 25.07.2014 | DE10054 | Maedhros | | Чикаго | 207.246.176.110 | 26.06.2014 | DE10062 | ssergw | | москва | 46.188.125.206 | 12.09.2014 | DE10086 | Вадим | | Тула | 109.111.26.176 | 26.02.2012 (10 rows) As you can see the b.id column above is empty - because these 10 users aren't banned. How to get a false value in that column instead of a String? And I am not after some coalesceor case expression, but am looking for "the proper" way to do such a query.
"IS NULL" and "IS NOT NULL" return a boolean, so this should make it easy. I think this is all you need? SELECT b.id IS NOT NULL as is_banned, -- The value of "is_banned" will be a boolean Not sure if you need the "NOT" or not, but you'll get a bool either way.
A CASE or COALESCE statement with an outer join IS the proper way to do this. select CASE WHEN b.id IS NULL THEN true ELSE false END AS banned, u.id, u.first_name, u.last_name, u.city, u.last_ip, to_char(u.login, 'DD.MM.YYYY') as day from pref_users u left outer join pref_ban2 b on u.id=b.id limit 10;