I was trying to make a function to work in db2:
CREATE FUNCTION TO_DATE8(DATE_STRING numeric(8,0))
RETURNS DATE
LANGUAGE SQL
IF DATE_STRING > 0 THEN
// ERROR ->
RETURN DATE ( TO_DATE ( SUBSTR ( DATE_STRING , 1 , 8 ) , 'YYYYMMDD' ) )
ELSE
RETURN DATE ( TO_DATE ( '00000000' , 'YYYYMMDD' ) )
END IF
END
ERROR: DATE IS NOT VALID
What to do?
The form of the procedure required seems to be like this (at least on the iSeries version):
CREATE FUNCTION TO_DATE8(DATE_STRING numeric(8,0))
RETURNS DATE
LANGUAGE SQL
BEGIN
RETURN(CASE WHEN DATE_STRING > 0 THEN DATE(SUBSTR(DATE_STRING, 1, 4) || '-' ||
SUBSTR(DATE_STRING, 5, 2) || '-' ||
SUBSTR(DATE_STRING, 7, 2))
ELSE DATE('0001-01-01')
END);
END
However:
Your procedure is misnamed (reading from a date-8, not to it).
Your DATE_STRING is not a string (or even a char), it's numeric. Please rename it to something that does not include the datatype (dateToConvert works)
You seem to want to return something that is not a valid date (all 0s). I'm returning *loval here, although it's possible it should actually be null.
I didn't put in enough checks for a valid date - this will blow up really easily.
If at all possible, the database should be changed to contain actual dates, not a numeric value. Disk is (relative to programmer/architect headaches) cheap.
You may also find a calendar file helpful, if the 8-digit numeric was one of the included columns.
For the benifit of others, this can be done in one line rather than a function:
CASE WHEN MYDATE = 0 THEN NULL ELSE DATE(INSERT(INSERT(LEFT(CHAR(MYDATE),8),5,0,'-'),8,0,'-')) END
MYDATE was 8 packed in my case.
Related
In MSSQL STR returns character data converted from numeric data. The character data is right-justified, with a specified length and decimal precision. SELECT STR(123.45, 6, 1) return ' 123.5' (not '123.5') in MSSQL. Also, SELECT STR(123.45, 2, 2) return '**' in MSSQL.
There is no direct corresponding Postgres function. You have a couple things going on: rounding at variable length, padding the result and result length validation. But easy enough to create your own SQL function: (see demo)
create or replace
function str(value_in numeric
,length_in integer default 7
,dec_len_in integer default 2
)
returns text
language sql
as $$
select case when length(val) > length_in
then rpad('*',length_in,'*') -- or just '**'
else lpad(val,length_in,'#') -- for demo using #, for real use ' '
end
from (select round(value_in, dec_len_in )::text) sq(val)
$$;
select service_code,service_cat_code,mobile_no,upper(applicant_name_eng) as name,to_char(license_date,'dd/mm/yyyy')as license_from,to_char(license_valid_upto,'dd/mm/yyyy')as license_to,Upper(license_no),district_code,taluk_code,CONCAT(address_building,', ', address_cityvillage,', ',address_locality,', ',address_landmark,', ',address_street) as address
from mst_license
WHERE cast(license_valid_upto as date) = case
WHEN license_valid_upto < now()
THEN
case
when license_valid_upto = '2021-06-30'
then 1 else 0
END
ELSE
case when license_valid_upto > now()
then 1 else 0
End
END
and Upper(license_no)='1SP146924BJP'
I want license valid should be either greater than now or if license valid less than now it must be with the date ''30/06/2021' but when i use above query i get error
ERROR: operator does not exist: date = integer
LINE 3: WHERE cast(license_valid_upto as date) = case
^
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
SQL state: 42883
Character: 418
Help me out guys
The main issue you have is that your case statement returns an integer (1 or 0) but you are trying to compare that to a date, which you cannot do as Postgres is a strict data typing. Even if it did work it would always be false (well except for 1969-12-31/1970-01-01). Moreover the case structure is not needed. The best/correct to compare dates is just use date values. Since you did not indicate the data type for column license_valid_upto so based on how it is used I'll assume it is timestamp with timezone as that is what NOW() returns. Your query becomes:
select service_code
, service_cat_code
, mobile_no
, upper(applicant_name_eng) as name
, to_char(license_date,'dd/mm/yyyy')as license_from
, to_char(license_valid_upto,'dd/mm/yyyy')as license_to
, upper(license_no) as license_no
, district_code
, taluk_code
, concat(address_building,', '
,address_cityvillage,', '
,address_locality,', '
,address_landmark,', '
,address_street) as address
from mst_license
where and upper(license_no)='1SP146924BJP'
and ( cast(license_valid_upto as date) > cast( now() as date)
or (cast (icense_valid_upto as date) < cast( now() as date)
and cast (icense_valid_upto as date) = date '2021-06-30'
)
);
Also, learn for format your queries for readability and so you do not need to scroll right. You, and others looking at your queries later will appreciate it later.
I have a stored procedure as under
ALTER PROCEDURE [dbo].[usp_testProc]
(#Product NVARCHAR(200) = '',
#BOMBucket NVARCHAR(100) = '')
--exec usp_testProc '','1'
AS
BEGIN
SELECT *
FROM Mytbl x
WHERE 1 = 1
AND (#Product IS NULL OR #Product = '' OR x.PRODUCT = #Product)
AND (#BOMBucket IS NULL OR #BOMBucket = '' OR CAST(x.BOMBucket AS NVARCHAR(100)) IN (IIF(#BOMBucket != '12+', #BOMBucket, '13,14')))
END
Everything else if working fine except when I am passing the bucket value as 12+ . It should ideally show the result for bucket 13 and 14. But the result set is blank.
I know IN expects values as ('13','14'). But somehow not able to fit it in the program.
You can express that logic in a Boolean expression.
...
(#BOMBucket <> '12+'
AND cast(x.BOMBucket AS nvarchar(100)) = #BOMBucket
OR #BOMBucket = '12+'
AND cast(x.BOMBucket AS nvarchar(100)) IN ('13', '14'))
...
But casting the column prevents indexes from being used. You rather should cast the other operand. Like in:
x.BOMBucket = cast(#BOMBucket AS integer)
But then you had the problem, that the input must not be a string representing an inter but can be any string. this would cause an error when casting. In newer SQL Server versions you could circumvent that by using try_cast() but not in 2012 as far as I know. Maybe you should rethink your approach overall and pass a table variable with the wanted BOMBucket as integers instead.
I am doing an SQL SELECT query but I have the error message:
"SQL Error [22007]: [SQL0181] A value of date, time, or timestamp
string is incorrect."
Here is my request:
SELECT *
FROM ROXDTA400.STKF0300 A
JOIN ROXDTA400.TABJ00141 B ON A.STNSIT = B.CDSITE
WHERE ( A.STNLIB <> '-- Trémie --'
AND A.STNSIT <> 40
AND DATE(LEFT(STNDAV,4) || '-' || substr(STNDAV,5,2) || '-' || RIGHT(STNDAV,2))
BETWEEN DATE('2019-01-01') AND DATE('2019-01-04') );
The problem seems to come from the date created with the STNDAV field, because if I replace with for example DATE ('2019-01-03'), it works.
DATE (LEFT (STNDAV, 4) || '-' || substr (STNDAV, 5,2) || '-' || RIGHT (STNDAV, 2)) Gives me the correct date format.
Where would the problem come from?
thank you,
Ensure that dates stored in STNDAV are valid. I mean, check there is any invalid date such as February 30th or '99999999'. If the source is an IBM i (iSeries or AS/400), it will be faster if you avoid functions in the WHERE portion, so STNDAV BETWEEN '20190101' AND '20190104' will perform better.
In PostgreSQL: I convert string to timestamp with to_timestamp():
select * from ms_secondaryhealthcarearea
where to_timestamp((COALESCE(update_datetime, '19900101010101'),'YYYYMMDDHH24MISS')
> to_timestamp('20121128191843','YYYYMMDDHH24MISS')
But I get this error:
ERROR: syntax error at end of input
LINE 1: ...H24MISS') >to_timestamp('20121128191843','YYYYMMDDHH24MISS')
^
********** Error **********
ERROR: syntax error at end of input
SQL state: 42601
Character: 176
Why? How to convert a string to timestamp?
One too many opening brackets. Try this:
select *
from ms_secondaryhealthcarearea
where to_timestamp(COALESCE(update_datetime, '19900101010101'),'YYYYMMDDHH24MISS') >to_timestamp('20121128191843','YYYYMMDDHH24MISS')
You had two opening brackets at to_timestamp:
where to_timestamp((COA.. -- <-- the second one is not needed!
#ppeterka has pointed out the syntax error.
The more pressing question is: Why store timestamp data as string to begin with? If your circumstances allow, consider converting the column to its proper type:
ALTER TABLE ms_secondaryhealthcarearea
ALTER COLUMN update_datetime TYPE timestamp
USING to_timestamp(update_datetime,'YYYYMMDDHH24MISS');
Or use timestamptz - depending on your requirements.
Another way to convert a string to a timestamp type of PostgreSql is the above,
SELECT to_timestamp('23-11-1986 06:30:00', 'DD-MM-YYYY hh24:mi:ss')::timestamp without time zone;
I had the same requirement as how I read the title. How to convert an epoch timestamp as text to a real timestamp. In my case I extracted one from a json object. So I ended up with a timestamp as text with milliseconds
'1528446110978' (GMT: Friday, June 8, 2018 8:21:50.978 AM)
This is what I tried. Just the latter (ts_ok_with_ms) is exactly right.
SELECT
data->>'expiration' AS expiration,
pg_typeof(data->>'expiration'),
-- to_timestamp(data->>'expiration'), < ERROR: function to_timestamp(text) does not exist
to_timestamp(
(data->>'expiration')::int8
) AS ts_wrong,
to_timestamp(
LEFT(
data->>'expiration',
10
)::int8
) AS ts_ok,
to_timestamp(
LEFT(
data->>'expiration',
10
)::int8
) + (
CASE
WHEN LENGTH(data->>'expiration') = 13
THEN RIGHT(data->>'expiration', 3) ELSE '0'
END||' ms')::interval AS ts_ok_with_ms
FROM (
SELECT '{"expiration": 1528446110978}'::json AS data
) dummy
This is the (transposed) record that is returned:
expiration 1528446110978
pg_typeof text
ts_wrong 50404-07-12 12:09:37.999872+00
ts_ok 2018-06-08 08:21:50+00
ts_ok_with_ms 2018-06-08 08:21:50.978+00
I'm sure I overlooked a simpler version of how to get from a timestamp string in a json object to a real timestamp with ms (ts_ok_with_ms), but I hope this helps nonetheless.
Update: Here's a function for your convenience.
CREATE OR REPLACE FUNCTION data.timestamp_from_text(ts text)
RETURNS timestamptz
LANGUAGE SQL AS
$$
SELECT to_timestamp(LEFT(ts, 10)::int8) +
(
CASE
WHEN LENGTH(ts) = 13
THEN RIGHT(ts, 3) ELSE '0'
END||' ms'
)::interval
$$;