NULL values in NOT NULL expression - postgresql

I have this ID column coming as NULL even though I have put a NOT NULL expression. It's not any empty string issue or any special character issue because I tried using this expression:
AND COALESCE(SYSTEM_ID,ASSET_NUMBER,SERIAL_NUMBER,'X') <>'X'
In my where clause (same in select) and X comes in output, why is this happening - I have provided sample select code. I am able to solve it by putting the filter in Having clause or using an outer query
SELECT COALESCE(COL1,COL2,COL3) AS ID,
COL4,
...
COL16
FROM TABLE
WHERE COALESCE(COL1,COL2,COL3) IS NOT NULL
GROUP BY
COL4,
...
COL16

Related

PostgreSQL query with unnest returns no result row for null values

I am trying to query the values that divides comma separated values in a column as different records ,I could not get the values if the column has null values, Below is the example
Table name: test
id,name,list
1,a, a1,b1
2,b, null
3,c,c1
Query which is used
select id,name,unnest(string_to_array(list,',')) from test;
Result:
1,a,a1
1,a,b1
3,c,c1
But I need to consider the null values and return the result as below ,I tried using coalesce but that did not work, kindly help me out with the solution
Expected result:
1,a,a1
1,a,b1
2,b,null
3,c,c1
Use unnest() with an outer join:
select t.id, t.name, u.element
from test t
left join unnest(string_to_array(t.list,',')) on true
order by t.id;

How to group by column and ignore NULLS within rows

Hi have a table like this:
langaugeCode titleText introText
en WeeklyTitle NULL
en NULL This text
de HereItIs NULL
de NULL ThereThere
Essentailly I want to group by language code but ignore the columns within a row that are NULL.
I would like to transform this table to:
langaugeCode titleText introText
en WeeklyTitle This text
de HereItIs ThereThere
You may try aggregating by languageCode and taking the maximum value of each column:
SELECT
languageCode,
MAX(titleText) AS titleText,
MAX(introText) AS introText
FROM yourTable
GROUP BY
languageCode;
Demo
This works because the MAX() aggregate function by default ignores NULL values. So, assuming you only have a single non NULL value for each language code, it would be retained during the aggregation.
You can add where condition for not null check for the columns you don't want null value.
I'd "merge" it by doing this:
Make a two tables (t1 and t2) and make a natural join on it.
SELECT
t2.languageCode, t1.titleText, t2.introText
FROM (SELECT
yt1.languageCode, yt1.titleText, yt1.introText
FROM yourTable yt1
GROUP BY
languageCode) t1, yourTable t2
WHERE
t1.languageCode = t2.languageCode
GROUP BY
t2.languageCode, t1.titleText, t2.introText;
Try it ;)

Update with ISNULL and operation

original query looks like this :
UPDATE reponse_question_finale t1, reponse_question_finale t2 SET
t1.nb_question_repondu = (9-(ISNULL(t1.valeur_question_4)+ISNULL(t1.valeur_question_6)+ISNULL(t1.valeur_question_7)+ISNULL(t1.valeur_question_9))) WHERE t1.APPLICATION = t2.APPLICATION;
I know you cannot update 2 tables in a single query so i tried this :
UPDATE reponse_question_finale t1
SET nb_question_repondu = (9-(COALESCE(t1.valeur_question_4,'')::int+COALESCE(t1.valeur_question_6,'')::int+COALESCE(t1.valeur_question_7)::int+COALESCE(t1.valeur_question_9,'')::int))
WHERE t1.APPLICATION = t1.APPLICATION;
But this query gaves me an error : invalid input syntax for integer: ""
I saw that the Postgres equivalent to MySQL is COALESCE() so i think i'm on the good way here.
I also know you cannot add varchar to varchar so i tried to cast it to integer to do that. I'm not sure if i casted it correctly with parenthesis at the good place and regarding to error maybe i cannot cast to int with coalesce.
Last thing, i can certainly do a co-related sub-select to update my two tables but i'm a little lost at this point.
The output must be an integer matching the number of questions answered to a backup survey.
Any thoughts?
Thanks.
coalesce() returns the first non-null value from the list supplied. So, if the column value is null the expression COALESCE(t1.valeur_question_4,'') returns an empty string and that's why you get the error.
But it seems you want something completely different: you want check if the column is null (or empty) and then subtract a value if it is to count the number of non-null columns.
To return 1 if a value is not null or 0 if it isn't you can use:
(nullif(valeur_question_4, '') is null)::int
nullif returns null if the first value equals the second. The IS NULL condition returns a boolean (something that MySQL doesn't have) and that can be cast to an integer (where false will be cast to 0 and true to 1)
So the whole expression should be:
nb_question_repondu = 9 - (
(nullif(t1.valeur_question_4,'') is null)::int
+ (nullif(t1.valeur_question_6,'') is null)::int
+ (nullif(t1.valeur_question_7,'') is null)::int
+ (nullif(t1.valeur_question_9,'') is null)::int
)
Another option is to unpivot the columns and do a select on them in a sub-select:
update reponse_question_finale
set nb_question_repondu = (select count(*)
from (
values
(valeur_question_4),
(valeur_question_6),
(valeur_question_7),
(valeur_question_9)
) as t(q)
where nullif(trim(q),'') is not null);
Adding more columns to be considered is quite easy then, as you just need to add a single line to the values() clause

QueryDSL coalesce in order by

I'm trying to join two tables and output them and sort them alphabetically by two fields like order by coalesce(tableA.name, tableB.name) (NOT order by tableA.name, tableB.name), so result should be something like:
tableA.name tableB.name
A null
B null
null C
D null
null E
In plain SQL it works fine but when I try to do it with QueryDSL it adds additional column to generated select statement and sorts only by first specified column:
//java code
query.orderBy(qTableA.name.coalesce(qTableB.name).asc());
//generated sql code
SELECT ...
COALESCE(tablea_.NAME, tableb_.NAME) AS col_9_0_
FROM ...
WHERE ...
ORDER BY tablea1_.NAME ASC
Can somebody tell why it does it like that and whether it is possible to make it work as I expect?
Try this:
final Coalesce<String> coalesce =
new Coalesce<>(String.class).add(optionalA).add(optionalB);
Use the coalesce in your select fields and so in your order by clause:
.orderBy(coalesce.asc()) // or desc()

postgres `order by` argument type

What is the argument type for the order by clause in Postgresql?
I came across a very strange behaviour (using Postgresql 9.5). Namely, the query
select * from unnest(array[1,4,3,2]) as x order by 1;
produces 1,2,3,4 as expected. However the query
select * from unnest(array[1,4,3,2]) as x order by 1::int;
produces 1,4,3,2, which seems strange. Similarly, whenever I replace 1::int with whatever function (e.g. greatest(0,1)) or even case operator, the results are unordered (on the contrary to what I would expect).
So which type should an argument of order by have, and how do I get the expected behaviour?
This is expected (and documented) behaviour:
A sort_expression can also be the column label or number of an output column
So the expression:
order by 1
sorts by the first column of the result set (as defined by the SQL standard)
However the expression:
order by 1::int
sorts by the constant value 1, it's essentially the same as:
order by 'foo'
By using a constant value for the order by all rows have the same sort value and thus aren't really sorted.
To sort by an expression, just use that:
order by
case
when some_column = 'foo' then 1
when some_column = 'bar' then 2
else 3
end
The above sorts the result based on the result of the case expression.
Actually I have a function with an integer argument which indicates the column to be used in the order by clause.
In a case when all columns are of the same type, this can work: :
SELECT ....
ORDER BY
CASE function_to_get_a_column_number()
WHEN 1 THEN column1
WHEN 2 THEN column2
.....
WHEN 1235 THEN column1235
END
If columns are of different types, you can try:
SELECT ....
ORDER BY
CASE function_to_get_a_column_number()
WHEN 1 THEN column1::varchar
WHEN 2 THEN column2::varchar
.....
WHEN 1235 THEN column1235::varchar
END
But these "workarounds" are horrible. You need some other approach than the function returning a column number.
Maybe a dynamic SQL ?
I would say that dynamic SQL (thanks #kordirko and the others for the hints) is the best solution to the problem I originally had in mind:
create temp table my_data (
id serial,
val text
);
insert into my_data(id, val)
values (default, 'a'), (default, 'c'), (default, 'd'), (default, 'b');
create function fetch_my_data(col text)
returns setof my_data as
$f$
begin
return query execute $$
select * from my_data
order by $$|| quote_ident(col);
end
$f$ language plpgsql;
select * from fetch_my_data('val'); -- order by val
select * from fetch_my_data('id'); -- order by id
In the beginning I thought this could be achieved using case expression in the argument of the order by clause - the sort_expression. And here comes the tricky part which confused me: when sort_expression is a kind of identifier (name of a column or a number of a column), the corresponding column is used when ordering the results. But when sort_expression is some value, we actually order the results using that value itself (computed for each row). This is #a_horse_with_no_name's answer rephrased.
So when I queried ... order by 1::int, in a way I have assigned value 1 to each row and then tried to sort an array of ones, which clearly is useless.
There are some workarounds without dynamic queries, but they require writing more code and do not seem to have any significant advantages.