PostgreSQL concatenate integer and real and add units - postgresql

I want to concatenate 4 columns in a PostgreSQL query and add units if columns aren't empty.
SELECT
CASE
WHEN a.length_m notnull THEN a.length_m || 'm'
WHEN a.area_ar notnull THEN a.area_ar || 'ar'
WHEN a.vol notnull THEN a.vol || 'cbm'
WHEN a.pcs notnull THEN a.pcs || 'pcs'
END as quant;
FROM actions a;
This works fine, but if I have values in two colums it only shows me the first one.
How must I change my query, so that a second WHEN gets evaluated?

Use COALESCE:
SELECT COALESCE(a.length_m ||'m', '') ||
COALESCE(a.area_ar ||'ar', '') ||
COALESCE(a.vol ||'cbm', '') ||
COALESCE(a.pcs ||'pcs', '') as quant
FROM actions a;

Related

PostgresQL: Find array length of output from ARRAY_AGG()

How do I count the number of distinct elements in an array object, created by ARRAY_AGG() in PostgresQL? Here's a toy example for discussion purposes:
SELECT ARRAY_AGG (first_name || ' ' || last_name) actors
FROM film
I have tried ARRAY_LENGTH(), LENGTH(), etc., like so:
SELECT ARRAY_LENGTH(a.actors)
FROM (SELECT ARRAY_AGG (first_name || ' ' || last_name) actors
FROM film) a;
But I get an error:
function array_length(integer[]) does not exist
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
Position: 208
So I tried (2):
SELECT ARRAY_LENGTH( CAST(COALESCE(a.actors, '0') AS integer) )
FROM (SELECT ARRAY_AGG (first_name || ' ' || last_name) actors
FROM film) a;
but I get the error:
malformed array literal: "0"
Detail: Array value must start with "{" or dimension information.
Position: 119
the function array_length(anyarray, int) require two elements, array and dimension for example:
Select array_length(array[1,2,3], 1);
Result:
3
If you are only dealing with a single dimension array, cardinality() is easier to use:
SELECT cardinality(ARRAY_LENGTH(a.actors))
FROM (
SELECT ARRAY_AGG (first_name || ' ' || last_name) actors
FROM film
) a;

How to add order by in string agg, when two columns are concatenated

SELECT string_agg( distinct a || '-' || b , ',' ORDER BY a,b)
FROM table;
The above sql giving error
ERROR: in an aggregate with DISTINCT, ORDER BY expressions must appear in argument list
For the documentation:
If DISTINCT is specified in addition to an order_by_clause, then all the ORDER BY expressions must match regular arguments of the aggregate; that is, you cannot sort on an expression that is not included in the DISTINCT list.
So try
select string_agg(distinct a || '-' || b, ',' order by a || '-' || b)
from a_table;
or use distinct in a derived table:
select string_agg(a || '-' || b , ',' order by a, b)
from (
select distinct a, b
from a_table
) s;

How do I find all the NUMERIC columns in a table and do a SUM() on them?

I have a few tables in Netezza, DB2 and PostgreSQL databases, for which I need to reconcile and the best way we have come out with is to do a SUM() across all the NUMERIC Table columns on all the 3 databases.
Does anyone have a quick and simple way to find all the COLUMNS which are either NUMERIC or INTEGER or BIGINT and then run a SUM() on all these?
For comparing the results, I can do it manually also, or if someone has a way to capture these results in a common table and automatically check the differences in the SUM?
For DB2 you can use this metadata which will help you to find out the data type for each column
SELECT
COLUMN_NAME || ' ' || REPLACE(REPLACE(DATA_TYPE,'DECIMAL','NUMERIC'),'CHARACTER','VARCHAR') ||
CASE
WHEN DATA_TYPE = 'TIMESTAMP' THEN ''
ELSE
' (' ||
CASE
WHEN CHARACTER_MAXIMUM_LENGTH IS NOT NULL THEN CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR(30))
WHEN NUMERIC_PRECISION IS NOT NULL THEN CAST(NUMERIC_PRECISION AS VARCHAR(30)) ||
CASE
WHEN NUMERIC_SCALE = 0 THEN ''
ELSE ',' || CAST(NUMERIC_SCALE AS VARCHAR(3))
END
ELSE ''
END || ')'
END || ',' "SQLCOL",
COLUMN_NAME,
DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, NUMERIC_SCALE, ORDINAL_POSITION
FROM SYSIBM.COLUMNS
WHERE TABLE_NAME = 'insert your table name'
AND TABLE_SCHEMA = 'insert your table schema'
ORDER BY ORDINAL_POSITION
For Netezza, I got the following query:
SELECT 0 AS ATTNUM, 'SELECT' AS SQL
UNION
SELECT ATTNUM, 'SUM(' || ATTNAME || ') AS S_' || ATTNAME || ',' AS COLMN
FROM _V_RELATION_COLUMN RC
WHERE NAME = '<table-name>'
AND FORMAT_TYPE= 'NUMERIC'
UNION
SELECT 10000 AS ATTNUM, ' 0 AS FLAG FROM ' || '<table-name>'
ORDER BY ATTNUM
Still looking how to do this across DB2 and PostgreSQL.

Why do two seemingly identical where clauses involving nulls produce different results

I'm trying to select all records that don't have a null in a particular column and it's value isn't in another table.
So this particular situation I want to get all 'Instructors' from an import table that aren't already in the Individuals table. Obviously I don't want any blank instructors. My first attempt I tried using in the where clause:
(Instructor IS NOT NULL OR Instructor <> '')
However the results still included all blank records. When I tried using
ISNULL(Instructor, '') <> ''
I got the desired result. I can't see how these two where clauses could possibly produce different results. To me it seems like ISNULL converting the value to empty string for comparison should have exactly the same outcome as comparing the column to null then to empty string. What am I missing here? I'm guessing it's to do with the oddness of null values.
Below are the full queries
SELECT * FROM [tempimporttblTrainingLog]
LEFT JOIN tblIndividual I ON [Instructor] = I.FirstName + ' ' + I.Surname
WHERE (I.FirstName + ' ' + I.Surname IS NULL) AND (Instructor IS NOT NULL OR Instructor <> '')
SELECT * FROM [tempimporttblTrainingLog]
LEFT JOIN tblIndividual I ON [Instructor] = I.FirstName + ' ' + I.Surname
WHERE (I.FirstName + ' ' + I.Surname IS NULL) AND (ISNULL(Instructor, '') <> '')
ISNULL(Instructor, '') <> '' (1)
is equivalent to
(Instructor IS NOT NULL AND Instructor <> '') (2)
not
(Instructor IS NOT NULL OR Instructor <> '') (3)
If Instructor IS NULL, (1) and (2) will return FALSE, when (3) returns TRUE.
Same for when Instructor = ''.
ISNULL(Instructor, '') <> ''
won't return blank records, and you have an OR in the
(Instructor IS NOT NULL OR Instructor <> '')
line, meaning it will return anything which isn't null as well as non blanks, did you mean to put an AND instead?

SQL if statement to fix issue

I have this select
insert into test(t_name)
(select users.first_name || '_' || users.last_name
|| '_' || to_date($values.report_date, 'YYYY.MM.DD')
from users where users.id = $session.user_id)
which works as supposed, except I want it if users.first_name/users.last_name are null and user_id=0 to insert another string 'admin' otherwise I get error because it violates not-null constraint on column t_name.
Any suggestions are appreciated. Thanks.
Use a CASE in the SELECT part:
insert into test (t_name)
select case
when users.first_name is null and users.last_name is null and users.id=0 then 'admin'
else users.first_name || '_' || users.last_name || '_' || to_date($values.report_date,'YYYY.MM.DD')
end
from users
where users.id = $session.user_id
It's not clear (to me) from the question if all conditions you mentioned have to be true or just one of them. Depending on what you want, you should change the ANDs to ORs in the first WHEN part.
(Note that there is no need to enclose the SELECT in () for an INSERT ... SELECT statement)
If you are using MySQL you can use IFNULL.
INSERT INTO test(t_name)
(
SELECT IFNULL(users.first_name || '_' || users.last_name || '_' || to_date($values.report_date, 'YYYY.MM.DD'), 'admin')
FROM users
WHERE users.id = $session.user_id
)
In MSSQL there is a similar ISNULL function, I assume that there is likewise solutions for other database systems.