Modify values within a column and row (PSQL) - postgresql

I get the following error for this query: [22P02] ERROR: invalid input syntax for type numeric: "."
select
date,
row_number () over () as RN,
case when (row_number() over ()) ='8' then '.' else (success/trials) end as "After_1M"
from trials
groupy by date;
Is there another way to indicate that a certain value in a ROWxCOLUMN combination should be adjusted?

Well your description certainly leaves a lot to be desired. But your query only needs slight modification to actually run. First off "groupy by date". I will assume it's just a typo. But a group by without an aggregate function generally doesn't do anything - and this is one of those. But I believe your attempting to get a row count by date. If so the you need the partition by and order by clauses in the in the row_number function. The other issue is in the expression. Each entry in the expression must return the same data type but in case it doesn't. The THEN condition returns character (.) while the ELSE returns a numeric (success/trials) which must define 2 numeric columns to be valid. So which needs to change? I will assume the later. Given this we wind up with:
select date
, row_number() over(partition by date order by trl_date) rn
, case when (row_number() over(partition by date order by trl_date)) = 8
then '.'
else (success/trials)::text
end as "After_1M"
from trials;
Note: Date is a very poor date is a very poor column name. It's a reserved word, as well as a data type.

Related

Postgresql - select column based on condition

In this query the 'Daily' in the case will be replaced by a variable. I am not able to make this query work. I want to have the date column being either a day, a week a month or a year based on the value of the variable. but it is giving me various errors..
CASE types date and double precison cannot be matched
syntax error near "as"
what am I doing wrong?
select
case 'Daily'
when 'Daily' then DATE(to_timestamp(e.startts)) as "Date",
when 'Weekly' then DATE_PART('week',to_timestamp(e.startts)) as "Date",
when 'Monthly' then to_char(to_timestamp(e.startts), 'mm/yyyy') as "Date",
when 'Yearly' then to_char(to_timestamp(e.startts), 'yyyy') as "Date",
end
sum(e.checked)
from entries e
WHERE
e.startts >= date_part('epoch', '2020-10-01T15:01:50.859Z'::timestamp)::int8
and e.stopts < date_part('epoch', '2021-11-08T15:01:50.859Z'::timestamp)::int8
group by "Date"
CASE ... END is an expression. An expression must have a well-defined data type, so PostgreSQL makes sure that the expressions in the THEN clause have the same data type (or at least compatible ones).
You would need a type cast, probably to text, in the first two branches:
... THEN CAST (date(to_timestamp(e.startts)) AS text)
But it would be much better to use to_char in all branches – there are format codes for everything you need.
An expression can have no alias, only an entry in the SELECT or FROM list can. So you need to append AS "Date" at the end of the CASE ... END expression, not somewhere in the middle.

PostgreSQL: Is it possible to sort the part of the string has datetime

I have the name of the column that contains the data sequence and datetime type as shown below:
DATA01_0003_20210126135705.zip
DATA01_0002_20210127135030.zip
DATA01_0004_20210126142913.zip
I want ORDER BY according to datetime, then the sequence string, is it possible?
DATA01_0002_20210127135030.zip
DATA01_0004_20210126142913.zip
DATA01_0003_20210126135705.zip
I tried the statement like below but it sort in sequence before datetime:
SELECT filename FROM tblData ORDER BY filename
DATA01_0002_20210127135030.zip
DATA01_0003_20210126135705.zip
DATA01_0004_20210126142913.zip
SELECT filename FROM tblData ORDER BY filename DESC
DATA01_0004_20210126142913.zip
DATA01_0003_20210126135705.zip
DATA01_0002_20210127135030.zip
in postgresql you can sort by expression.
to sort by the file's timestamp, it is necessary to extract that from the string. based on your examples, i am going to guess that the text after the last _ will have the date. if that assumption holds, then the following will sort by date
SELECT filename from tbldata
order by reverse(split_part(reverse(filename), '_', 1))
if the number of _ is fixed, then the trick with two reverses is unnecessary. you can instead order by split_part(filename, '_', 3)

ORDER BY CASE & Ordinal not working

Sorting column #7 as an example -
This code does not sort data at all:
ORDER BY CASE WHEN '1'='2' THEN 5
WHEN '1'='1' THEN 7
ELSE 13 END
If I change it to a hard-coded ordinal it works:
ORDER BY 7
As long as the respective expressions in the SELECT list are of the same type, you can do it by using the expressions themselves instead of the SELECT list number:
SELECT expression1, expression2, ...
...
ORDER BY CASE
WHEN 1=2
THEN expression5
WHEN 1=1
THEN expression7
ELSE expression13
END;
If the data types are not the same, season with type casts.
Your query does not work because only integer literals can be used as column numbers in ORDER BY. In all other cases, an integer just stands for its constant value.
If it were not like this, ORDER BY expressions could easily become ambiguous. Look at the following:
... ORDER BY intcol + 3;
Should that mean “add three” or “add expression number three from the SELECT list”?

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.

Prepare dynamic case statement using PostgreSQL 9.3

I have the following case statement to prepare as a dynamic as shown below:
Example:
I have the case statement:
case cola
when cola between '2001-01-01' and '2001-01-05' then 'G1'
when cola between '2001-01-10' and '2001-01-15' then 'G2'
when cola between '2001-01-20' and '2001-01-25' then 'G3'
when cola between '2001-02-01' and '2001-02-05' then 'G4'
when cola between '2001-02-10' and '2001-02-15' then 'G5'
else ''
end
Note: Now I want to create dynamic case statement because of the values dates and name passing as a parameter and it may change.
Declare
dates varchar = '2001-01-01to2001-01-05,2001-01-10to2001-01-15,
2001-01-20to2001-01-25,2001-02-01to2001-02-05,
2001-02-10to2001-02-15';
names varchar = 'G1,G2,G3,G4,G5';
The values in the variables may change as per the requirements, it will be dynamic. So the case statement should be dynamic without using loop.
You may not need any function for this, just join to a mapping data-set:
with cola_map(low, high, value) as (
values(date '2001-01-01', date '2001-01-05', 'G1'),
('2001-01-10', '2001-01-15', 'G2'),
('2001-01-20', '2001-01-25', 'G3'),
('2001-02-01', '2001-02-05', 'G4'),
('2001-02-10', '2001-02-15', 'G5')
-- you can include as many rows, as you want
)
select table_name.*,
coalesce(cola_map.value, '') -- else branch from case expression
from table_name
left join cola_map on table_name.cola between cola_map.low and cola_map.high
If your date ranges could collide, you can use DISTINCT ON or GROUP BY to avoid row duplication.
Note: you can use a simple sub-select too, I used a CTE, because it's more readable.
Edit: passing these data (as a single parameter) can be achieved by passing a multi-dimensional array (or an array of row-values, but that requires you to have a distinct, predefined composite type).
Passing arrays as parameters can depend on the actual client (& driver) you use, but in general, you can use the array's input representation:
-- sql
with cola_map(low, high, value) as (
select d[1]::date, d[2]::date, d[3]
from unnest(?::text[][]) d
)
select table_name.*,
coalesce(cola_map.value, '') -- else branch from case expression
from table_name
left join cola_map on table_name.cola between cola_map.low and cola_map.high
// client pseudo code
query = db.prepare(sql);
query.bind(1, "{{2001-01-10,2001-01-15,G2},{2001-01-20,2001-01-25,G3}}");
query.execute();
Passing each chunk of data separately is also possible with some clients (or with some abstractions), but this is highly depends on your driver/orm/etc. you use.