Why do I have to do a type cast in the ELSE part of a PostgreSQL update statement using a CASE, but not in the WHEN part? - postgresql

I have to update a newly created metadata table (which holds a 1-1 relations with a person table) in PostgreSQL (>=12):
UPDATE public.metadata m
SET print_status =
CASE
WHEN p.print IS NOT NULL THEN 'done'
ELSE 'not done'
END
FROM public.people p
WHERE m.fk_people_id = p.id;
but I face this error:
ERROR: column "print_status" is of type print_statuses but expression is of type text
LINE 18: CASE
^
HINT: You will need to rewrite or cast the expression.
SQL state: 42804
Character: 477
print_statuses is an enum containing ('done', 'doing', 'not done'). And the print_status column of the metadata table is of type print_statuses (it also defaults to 'not done', so I wouldn't need the ELSE part anymore I guess).
It seems I do have to type cast the 'not done' text in the ELSE part of the CASE statement like this for this query to work:
UPDATE public.metadata m
SET print_status =
CASE
WHEN p.print IS NOT NULL THEN 'done'
ELSE 'not done'::print_statuses
END
FROM public.people p
WHERE m.fk_people_id = p.id;
Why do I have to do a type cast here, and not when using a simple UPDATE statement as in:
UPDATE public.metadata m SET print_status = 'not done' FROM public.people p WHERE g.fk_people_id = i.id;
?

Related

Postgres CASE WHEN IN query

I've attempted the below query in postgres 11
CASE
WHEN planning_status::varchar in (('Application Under Consideration'::varchar,'Appeal In Progress'::varchar)) then 'this_label'
WHEN planning_status::varchar = 'Approved' and actual_completion_date is not null then 'that_label'
ELSE 'reject_label'
END
I can't get the query to run, initially getting error on mismatching operator to record type. I also attempted IN (VALUES()) method. The below works:
CASE
WHEN planning_status = 'Application Under Consideration' then 'this_label'
WHEN planning_status = 'Appeal In Progress' then 'this_label'
WHEN planning_status = 'Application Received' then 'this_label'
WHEN planning_status = 'Approved' and actual_completion_date is not null then 'that_label'
ELSE 'reject_label'
END
Is it possible to use the IN query within a CASE WHEN query with strings. The strings are categorical but not stored as such
The problem are the double parentheses:
-- this doesn't work:
SELECT CASE WHEN 1 IN ((1, 2)) THEN 'works' ELSE 'weird' END;
ERROR: operator does not exist: integer = record
LINE 1: SELECT CASE WHEN 1 IN ((1, 2)) THEN 'works' ELSE 'weird' END...
^
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
-- this works:
SELECT CASE WHEN 1 IN (1, 2) THEN 'works' ELSE 'weird' END;
case
═══════
works
(1 row)
The reason is that in the first statement, the inner parentheses are forming a composite type (record) with two elements, and PostgreSQL doesn't know how to compare that to the integer 1.
If the = version works, then this should work:
(CASE WHEN planning_status IN ('Application Under Consideration', 'Appeal In Progress', 'Application Received')
THEN 'this_label'
WHEN planning_status = 'Approved' and actual_completion_date is not null
THEN 'that_label'
ELSE 'reject_label'
END)
There should be no need for an explicit type conversion. If this doesn't work, what is the type of planning_status?

Unexpected end of string when removing key from hstore

When I am removing a key from a HSTORE, I get the error 'Unexpected end of string':
DB=# UPDATE mytable SET properties = properties - 'key' where label = '9912345678';
ERROR: Unexpected end of string
LINE 1: UPDATE mytable SET properties = properties - 'key' where ...
When I explicitly cast that string, it does work:
DB=# UPDATE mytable SET properties = properties - 'key'::text where label = '9912345678';
UPDATE 1
Why does it give this error message? Isn't 'key' a TEXT column? Or at least a string with a expected end?
The operator is overloaded so the right-hand operand should be explicitly cast. Note the example usage for text, text[] and hstore in the documentation:
'a=>1, b=>2, c=>3'::hstore - 'b'::text
'a=>1, b=>2, c=>3'::hstore - ARRAY['a','b']
'a=>1, b=>2, c=>3'::hstore - 'a=>4, b=>2'::hstore
In the statement
UPDATE mytable SET properties = properties - 'key' where label = '9912345678';
the string 'key' may be resolved as text or hstore. The first choice of the hstore algorithm is hstore, so the statement is resolved to
UPDATE mytable SET properties = properties - 'key'::hstore where label = '9912345678';
and raises the error
ERROR: Unexpected end of string
though the error message could be more informative, like Unexpected end of string while parsing hstore constant.

ELSE value not executed in CASE expression PostgreSQL

I'm using CASE expression to display "NamaAyah" or "NamaIbu" or "NamaWali", and if all of them is empty, the default value will display "ORTU-'NomorPokok' ".
But the default value not displayed, it just displays symbol "-" in my table. I think the value in ELSE statement not executed.
Postgre Version : PostgreSQL 9.4.15
This is my code
SELECT
"MahasiswaID" AS "PERSONID","NomorPokok" AS "KODE",
UPPER(CASE
WHEN "NamaAyah" <> '' THEN "NamaAyah"
WHEN "NamaIbu" <> '' THEN "NamaIbu"
WHEN "NamaWali" <> '' THEN "NamaWali"
ELSE 'ORTU'||'-'||"NomorPokok"
END) AS "NAMALENGKAP"
FROM "MasterMahasiswa" ORDER BY "KODE"
and this is the result
The expression you have can simpler be:
ELSE 'ORTU-'||"NomorPokok"
Apart from that, the only reasonable explanation for what you display is that there are literal - in one or more of your columns "NamaAyah", "NamaIbu" and "NamaWali". Did you check that?

Postgresql SQLSTATE[42P18]: Indeterminate datatype with PDO and CONCAT

I'm having issues with CONCAT() when used on a WHERE, in PDO.
The code:
<?php
require_once('config.php');
$fdate = '01/01/2010';
$tdate = '31/12/2030';
$identification = '';
$count = "SELECT count(*) as total FROM ( select time_id from doc_sent WHERE date >= :fdate AND date <= :tdate AND identification LIKE concat('%',:identification,'%') ) x;";
//$count = "SELECT count(*) as total FROM ( select time_id from doc_sent WHERE date >= :fdate AND date <= :tdate ) x;";
$stmt_count_row_main_table = $pdo->prepare($count);
$stmt_count_row_main_table->execute(['fdate' => $fdate, 'tdate' => $tdate, 'identification' => $identification]);
//$stmt_count_row_main_table->execute(['fdate' => $fdate, 'tdate' => $tdate]);
$count_row_main_table = $stmt_count_row_main_table->fetch();
print_r( $count_row_main_table);
?>
The code works when the 'identification' part is commented.
When I'm trying to use CONCAT(), it doesn't.
I tried many "version" of CONCAT() (and read many other questions, like this one: How do I create a PDO parameterized query with a LIKE statement? ) but I am always referring to the main documentation:
https://www.postgresql.org/docs/9.1/static/functions-string.html
Which say:
concat('abcde', 2, NULL, 22) --> abcde222
The FULL error when I use CONCAT() is:
PHP Fatal error: Uncaught PDOException: SQLSTATE[42P18]: Indeterminate datatype: 7 ERROR: could not determine data type of parameter $3 in /var/www/pdo-reporter/show.php:17\nStack trace:\n#0 /var/www/pdo-reporter/show.php(17): PDOStatement->execute(Array)\n#1 {main}\n thrown in /var/www/pdo-reporter/show.php on line 17
What's wrong with my code?
CONCAT is a function that takes a VARIADIC argument list, which means that internally postgres will convert them into an array of the same type.
postgres=# \df concat
List of functions
Schema | Name | Result data type | Argument data types | Type
------------+--------+------------------+---------------------+------
pg_catalog | concat | text | VARIADIC "any" | func
When trying to resolve the input type to a single type, the SQL parser fails. It can be reproduced in this simpler form:
postgres=# PREPARE p AS select concat('A', $1);
ERROR: could not determine data type of parameter $1
The parser can't figure out the datatype of $1 so it errs on the side of caution.
One easy solution is to cast the parameter as text:
postgres=# PREPARE p AS select concat($1::text);
PREPARE
or with the CAST operator:
postgres=# PREPARE p AS select concat(cast($1 as text));
PREPARE
I haven't tested with PDO but presumably it would work (given how it deals with parameters to produce prepared statements) to change the query to:
"...identification LIKE '%' || :identification || '::text%'..."
or use the '||' operator instead of concat in the query:
identification LIKE '%' || :identification || '%'
EDIT: BTW if you want to find that a parameter :X is a substring of identification, this clause is more secure: strpos(identification, :X) > 0, because :X may contain '%' or '_' without causing any side-effect in the match, contrary to what happens with LIKE.

PostgreSQL: return message after count = 0

I have maybe easy question, but I'm completely stucked.
I have script
SELECT COALESCE(COUNT(id), 0) as MyFiels from table
It works fine and when I have zero value it shows 0.
But I want that instead of 0, I can see one line = "NO RESULTS" for example.
I tried:
SELECT COALESCE(to_char(COUNT(id), 'NO RESULT')) as MyFiels from table
And PostgreSQL shows error message:
ERROR: "E" is not supported
SQL state: 0A000
Where I'm incorrect? Any ideas?
I see what is the error, you are trying to use coalesce to convert 0 to string, and coalesce convert null to something. You need use a CASE
SELECT CASE WHEN COUNT(*) = 0 THEN 'NO RESULT'
ELSE CAST(COUNT(*) as TEXT)
END as field
FROM Table