How to cast varchar to boolean - postgresql

I have a variable 'x' which is varchar in staging table, but it is set to boolean in target table which has 'true' and 'false' values. How can I convert varchar to boolean in postgresql?

If the varchar column contains one of the strings (case-insensitive):
t, true, y, yes, on, 1
f, false, n, no, off, 0
you can simply cast it to boolean, e.g:
select 'true'::boolean, 'false'::boolean;
bool | bool
------+------
t | f
(1 row)
See SQLFiddle.

For Redshift, I had the best luck with the following:
SELECT DECODE(column_name,
'false', '0',
'true', '1'
)::integer::boolean from table_name;
This simply maps the varchar strings to '0' or '1' which Redshift can then cast first to integers, then finally to boolean.
A big advantage to this approach is that it can be expanded to include any additional strings which you would like to be mapped. i.e:
SELECT DECODE(column_name,
'false', '0',
'no', '0',
'true', '1',
'yes', '1'
)::integer::boolean from table_name;
You can read more about the DECODE method here.

In aws redshift unfortunately #klin answer doesn't work as mentioned by others. Inspired in the answer of #FoxMulder900, DECODE seems the way to go but there is no need to cast it to an integer first:
SELECT DECODE(original,
'true', true, -- decode true
'false', false, -- decode false
false -- an optional default value
) as_boolean FROM bar;
The following works:
WITH bar (original) AS
(SELECT 'false' UNION SELECT 'true' UNION SELECT 'null') -- dumb data
SELECT DECODE(original,
'true', true, -- decode true
'false', false, -- decode false
false -- an optional default value
) as_boolean FROM bar;
which gives:
original
as_boolean
false
false
null
false
true
true
I hope this helps redshift users.

For old PostgreSQL versions and in Redshift casting won't work but the following does:
SELECT boolin(textout('true'::varchar)), boolin(textout('false'::varchar));
See SQLFiddle also see the discussion on the PostgreSQL list.

If you can assume anything besides true is false, then you could use:
select
column_name = 'true' column_name_as_bool
from
table_name;

Related

Approaches to execute PostgreSQL's concat() instead of || in JOOQ?

The ||-operator and the concat(...)-function in PostgreSQL behave differently.
select 'ABC'||NULL||'def';
-- Result: NULL
select concat('ABC', NULL, 'def');
-- Result: 'ABCdef'
concat(...) ignores NULL values, but a NULL whithin a || expression makes the whole result become NULL.
In JOOQ, the DSL.concat() in the PostgreSQL dialect renders expressions using the ||-operator:
Java: dsl.select(
DSL.concat(
DSL.inline("ABC"),
DSL.inline(null, SQLDataType.VARCHAR),
DSL.inline("def"))
).execute();
SQL: select ('ABC' || null || 'def')
Result: NULL
I am looking for (elegant?) ways to invoke the concat(...)-function instead of the ||-operator via JOOQ in PostgreSQL:
Java: dsl.select(???).execute();
SQL: select concat('ABC', null, 'def')
Result: 'ABCdef'
I found two ways to achieve the posed objective.
Approach #1:
dsl.select(
field(
"concat({0})",
SQLDataType.VARCHAR,
list(
inline("ABC"),
inline(null, SQLDataType.VARCHAR),
inline("def")
)
)
).execute();
This has to the intended behavior, but necessitates the in my eyes ugly "concat({0})". A more elegant approach from my point of view is:
Approach #2:
dsl.select(
function(
"concat",
SQLDataType.VARCHAR,
inline("ABC"),
inline(null, SQLDataType.VARCHAR),
inline("def")
)
).execute();
This solution does not involve inline SQL with placeholders as approach #1. Why JOOQ generates || instead of concat(...) in the first place is still to be expounded, though.

PostgreSQL ERROR: invalid input syntax for integer: "1e+06"

The full error message is:
ERROR: invalid input syntax for integer: "1e+06"
SQL state: 22P02
Context: In PL/R function sample
The query I'm using is:
WITH a as
(
SELECT a.tract_id_alias,
array_agg(a.pgid ORDER BY a.pgid) as pgids,
array_agg(a.sample_weight_geo ORDER BY a.pgid) as block_weights
FROM results_20161109.block_microdata_res_joined a
WHERE a.tract_id_alias in (66772, 66773, 66785, 66802, 66805, 66806, 66813)
AND a.bldg_count_res > 0
GROUP BY a.tract_id_alias
)
SELECT NULL::INTEGER agent_id,
a.tract_id_alias,
b.year,
unnest(shared.sample(a.pgids,
b.n_agents,
1 * b.year,
True,
a.block_weights)
) as pgid
FROM a
LEFT JOIN results_20161109.initial_agent_count_by_tract_res_11 b
ON a.tract_id_alias = b.tract_id_alias
ORDER BY b.year, a.tract_id_alias, pgid;
And the shared.sample function I'm using is:
CREATE OR REPLACE FUNCTION shared.sample(ids bigint[], size integer, seed integer DEFAULT 1, with_replacement boolean DEFAULT false, probabilities numeric[] DEFAULT NULL::numeric[])
RETURNS integer[] AS
$BODY$
set.seed(seed)
if (length(ids) == 1) {
s = rep(ids,size)
} else {
s = sample(ids,size, with_replacement,probabilities)
}
return(s)
$BODY$
LANGUAGE plr VOLATILE
COST 100;
ALTER FUNCTION shared.sample(bigint[], integer, integer, boolean, numeric[])
OWNER TO "server-superusers";
I'm pretty new to this stuff, so any help would be appreciated.
Not a problem of the function. Like the error messages says: The string '1e+06' cannot be cast to integer.
Obviously, the columns n_agents in your table results_20161109.initial_agent_count_by_tract_res_11 is not an integer column. Probably type text or varchar? (That info would help in your question.)
Either way, the assignment cast does not work for the target type integer. But it does for numeric:
Does not work:
SELECT '1e+06'::text::int; -- error as in question
Works:
SELECT '1e+06'::text::numeric::int;
If my assumptions hold, you can use this as stepping stone.
Replace b.n_agents in your query with b.n_agents::numeric::int.
It's your responsibility that numbers stay in integer range, or you get the next exception.
If that did not nail it, you need to look into function overloading:
Is there a way to disable function overloading in Postgres
And function type resolution:
PostgreSQL function call
The schema search path is relevant in many related cases, but you did schema-qualify all objects, so we can rule that out.
How does the search_path influence identifier resolution and the "current schema"
Your query generally looks good. I had a look and only found minor improvements:
SELECT NULL::int AS agent_id -- never omit the AS keyword for column alias
, a.tract_id_alias
, b.year
, s.pgid
FROM (
SELECT tract_id_alias
, array_agg(pgid) AS pgids
, array_agg(sample_weight_geo) AS block_weights
FROM ( -- use a subquery, cheaper than CTE
SELECT tract_id_alias
, pgid
, sample_weight_geo
FROM results_20161109.block_microdata_res_joined
WHERE tract_id_alias IN (66772, 66773, 66785, 66802, 66805, 66806, 66813)
AND bldg_count_res > 0
ORDER BY pgid -- sort once in a subquery. cheaper.
) sub
GROUP BY 1
) a
LEFT JOIN results_20161109.initial_agent_count_by_tract_res_11 b USING (tract_id_alias)
LEFT JOIN LATERAL
unnest(shared.sample(a.pgids
, b.n_agents
, b.year -- why "1 * b.year"?
, true
, a.block_weights)) s(pgid) ON true
ORDER BY b.year, a.tract_id_alias, s.pgid;

How to extract letters from a string using Firebird SQL

I want to implement a stored procedure that extract letters from a varchar in firebird.
Example :
v_accountno' is of type varchar(50) and has the following values
accountno 1 - 000023208821
accountno 2 - 390026826850868140H
accountno 3 - 0700765001003267KAH
I want to extract the letters from v_accountno and output it in o_letter.
In my example: o_letter will store H for accountno 2 and KAH for accountno 3.
I tried the following stored procedure, which obviously won't work for accountno 3. (Please help).
CREATE OR ALTER PROCEDURE SP_EXTRACT_LETTER
returns (
o_letter varchar(50))
as
declare variable v_accountno varchar(50);
begin
v_accountno = '390026826850868140H';
if (not (:v_accountno similar to '[[:DIGIT:]]*')) then
begin
-- My SP won't work in for accountno 3 '0700765001003267KAH'
v_accountno = longsubstr(v_accountno, strlen(v_accountno), strlen(v_accountno));
o_letter = v_accountno;
end
suspend;
end
One solution would be to replace every digits with empty string like:
o_letter = REPLACE(v_accountno, '0', '')
o_letter = REPLACE(o_letter, '1', '')
o_letter = REPLACE(o_letter, '2', '')
...
Since Firebird 3, you can use substring for this, using its regex facility (using the similar clause):
substring(v_accountno similar '[[:digit:]]*#"[[:alpha:]]*#"' escape '#')
See also this dbfiddle.

PostgreSQL ERROR: INSERT has more target columns than expressions, when it doesn't

So I'm starting with this...
SELECT * FROM parts_finishing;
...I get this...
id, id_part, id_finish, id_metal, id_description, date,
inside_hours_k, inside_rate, outside_material
(0 rows)
...so everything looks fine so far so I do this...
INSERT INTO parts_finishing
(
id_part, id_finish, id_metal, id_description,
date, inside_hours_k, inside_rate, outside_material
) VALUES (
('1013', '6', '30', '1', NOW(), '0', '0', '22.43'),
('1013', '6', '30', '2', NOW(), '0', '0', '32.45'));
...and I get...
ERROR: INSERT has more target columns than expressions
Now I've done a few things like ensuring numbers aren't in quotes, are in quotes (would love a table guide to that in regards to integers, numeric types, etc) after I obviously counted the number of column names and values being inserted. I also tried making sure that all the commas are commas...really at a loss here. There are no other columns except for id which is the bigserial primary key.
Remove the extra () :
INSERT INTO parts_finishing
(
id_part, id_finish, id_metal, id_description,
date, inside_hours_k, inside_rate, outside_material
) VALUES
('1013', '6', '30', '1', NOW(), '0', '0', '22.43')
, ('1013', '6', '30', '2', NOW(), '0', '0', '32.45')
;
the (..., ...) in Postgres is the syntax for a tuple literal; The extra set of ( ) would create a tuple of tuples, which makes no sense.
Also: for numeric literals you don't want the quotes:
(1013, 6, 30, 1, NOW(), 0, 0, 22.43)
, ...
, assuming all these types are numerical.
I had a similar problem when using SQL string composition with psycopg2 in Python, but the problem was slightly different. I was missing a comma after one of the fields.
INSERT INTO parts_finishing
(id_part, id_finish, id_metal)
VALUES (
%(id_part)s <-------------------- missing comma
%(id_finish)s,
%(id_metal)s
);
This caused psycopg2 to yield this error:
ERROR: INSERT has more target columns than expressions.
This happened to me in a large insert, everything was ok (comma-wise), it took me a while to notice I was inserting in the wrong table of course the DB does not know your intentions.
Copy-paste is the root of all evil ... :-)
I faced the same issue as well.It will be raised, when the count of columns given and column values given is mismatched.
I have the same error on express js with PostgreSQL
I Solved it. This is my answer.
error fire at the time of inserting record.
error occurred due to invalid column name with values passing
error: INSERT has more target columns than expressions
ERROR : error: INSERT has more target columns than expressions
name: 'error',
length: 116,
severity: 'ERROR',
code: '42601',
detail: undefined,
hint: undefined,
position: '294',
internalPosition: undefined,
internalQuery: undefined,
where: undefined,
schema: undefined,
table: undefined,
column: undefined,
dataType: undefined,
constraint: undefined,
file: 'analyze.c',
line: '945',
here is my code dome
INSERT INTO student(
first_name, last_name, email, phone
)
VALUES
($1, $2, $3, $4),
values
: [ first_name,
last_name,
email,
phone ]
IN my case there was syntax error in sub query.

Modifying a SQL query within the Select statement

I have a stored proc that is called from my asp.net page. The field "RecordUpdated" returns TRUE or FALSE. I need that column to return YES (if true) or NO (if false)
How would I do that?
SET #SQL = 'SELECT RecID, Vendor_Number, Vendor_Name, Invoice_Number, Item_Number, RecordAddDate, RecordUpdated FROM Vendor_Invoice_Log'
SET #SQL = #SQL + ' ORDER BY Item_Number, Vendor_Number
PRINT #SQL
If all you are doing is trying to change TRUE to YES and FALSE to NO, you can do this:
select case
when recordupdated = true then 'YES'
when recordupdated = false then 'NO'
end recordupdates ...
Of course your code doesn't actually execute, so I am uncertain why you showed lines 2 and 3.
In situations where you need to return one or the other of something when doing a SELECT, the
CASE
WHEN true
THEN YES
WHEN false
THEN NO
END AS RecordUpdated
is pretty useful.
Sounds like a job for CASE()...