The condition in the when clause containing comparision of TIMESTAMP and its trunc() version is apparently evaluated differently than when it is in the where clause.
I am working in Oracle SQL developers. I have a table which has timestamp(6) attribute. I was told that the timestamp attribute has the time dimension always empty (00:00:00,000000). I wanted to check this and I got to interesting problem.
When I use where clause
Select * from CUSTOMER_DATA
where trunc(DAT_CUSTOMER_SINCE) <> DAT_CUSTOMER_SINCE
I get indeed zero rows.
But when I want to see the attributes as truncated, I use Case-when clause
CASE WHEN trunc(DAT_CUSTOMER_SINCE) = DAT_CUSTOMER_SINCE then
trunc(DAT_CUSTOMER_SINCE)
else DAT_CUSTOMER_SINCE
end
from CUSTOMER_DATA
I get output in the form of timestamp, i.e. as if condition was false.
Here is some sample table/data:
create table CUSTOMER_DATA(
DAT_CUSTOMER_SINCE TIMESTAMP(6),
Another_text VARCHAR2(50 CHAR)
)
--select * from customer_data;
Insert into customer_data values(TO_TIMESTAMP('2019-04-15 00:00:00,000000', 'YYYY-MM-DD HH24:MI:SS,FF6'),'Not important');
select
CASE WHEN trunc(DAT_CUSTOMER_SINCE) = DAT_CUSTOMER_SINCE then
trunc(DAT_CUSTOMER_SINCE)
else DAT_CUSTOMER_SINCE
end
from CUSTOMER_DATA;
select trunc(DAT_CUSTOMER_SINCE) from customer_data
where DAT_CUSTOMER_SINCE=trunc(DAT_CUSTOMER_SINCE);
My question is, why this comparison works in the where clause but doesn't work in the Case-when clause?
Related
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.
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.
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.
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.
I need to further refine my stored proc resultset from this post, I need to filter my resultset to display only records where emailaddr is NULL (meaning display only records that have Invoice_DeliveryType value of 'N' ).
Among numerous queries, I have tried:
select
Invoice_ID, 'Unknown' as Invoice_Status,
case when Invoice_Printed is null then '' else 'Y' end as Invoice_Printed,
case when Invoice_DeliveryDate is null then '' else 'Y' end as Invoice_Delivered,
(case when Invoice_DeliveryType <> 'USPS' then ''
when exists (Select 1
from dbo.Client c
Where c.Client_ID = SUBSTRING(i.Invoice_ID, 1, 6) and
c.emailaddr is not null
)
then 'Y'
else 'N'
end)
Invoice_ContactLName + ', ' + Invoice_ContactFName as ContactName,
from
dbo.Invoice
left outer join
dbo.fnInvoiceCurrentStatus() on Invoice_ID = CUST_InvoiceID
where
CUST_StatusID = 7
AND Invoice_ID = dbo.Client.Client_ID
AND dbo.client.emailaddr is NULL
order by
Inv_Created
but I get an error
The conversion of the nvarchar value '20111028995999' overflowed an int column
How can I get the stored procedure to only return records with DeliveryType = 'N' ?
Trying selecting the stored proc results into a temp table, then select
* from #TempTable
We could really do with a schema definition to get this problem resolved.
It appears that there is an implicit conversion occurring within one of your case statements, but without the schema def's it's difficult to track down which one.
You can't safely mix datatypes in CASE expressions, unless you are absolutely sure that any implicit conversions will work out OK you should make the conversions explicit.
Judging by the error message seeming to include something that could be a date represented as a string(20111028) plus some kind of other data ?time?(995999) it may be something to do with Invoice_DeliveryDate, but this is a shot in the dark without more details.