MS SQL arithmetic overflow after datetime comparison in where clause - tsql

i have a strange issue by using a date comparision on ms sql server 2014.
SELECT * FROM (SELECT AKTDET.*, DATEDIFF(day,aktdet.Bezahlt_am, getdate()- 50) as datumsdifferenz FROM (SELECT Convert(datetime,AKTDET.von_num - 2.0,121) as [Bezahlt_am], AKT.KENNUNG, AKTDET.* FROM AKT INNER JOIN AKTDET ON AKT.DSN = AKTDET.AKT_DSN Inner JOin FLDART ON AKTDET.Fldart_Dsn = FLDART.DSN WHERE FLDART.Kürzel = 'bezahlt am') AS AKTDET ) AS AKTDET WHERE datumsdifferenz > 0.0
fails every time with "Arithmetischer Überlauffehler beim Konvertieren von expression in den datetime-Datentyp." translation: "Arithmetic overflow error when converting expression to datetime data type."
if i don't use the where clause everything is fine. how could this happen?
SELECT * FROM (SELECT AKTDET.*, DATEDIFF(day,aktdet.Bezahlt_am, getdate()- 50) as datumsdifferenz FROM (SELECT Convert(datetime,AKTDET.von_num - 2.0,121) as [Bezahlt_am], AKT.KENNUNG, AKTDET.* FROM AKT INNER JOIN AKTDET ON AKT.DSN = AKTDET.AKT_DSN Inner JOin FLDART ON AKTDET.Fldart_Dsn = FLDART.DSN WHERE FLDART.Kürzel = 'bezahlt am') AS AKTDET ) AS AKTDET
the data seems to be completely correct. all values are obiously correct date values.
here is a example of rows:
datumsdifferenz Bezahlt_am KENNUNG AKT_DSN
700 2016-12-21 00:00:00.000 340 690837DC-C521-47A7-B845-0B3036CADA07
391 2017-10-26 00:00:00.000 1887 27BC0276-0FAF-4787-BC69-4F7CC8F4D44A
391 2017-10-26 00:00:00.000 1887 27BC0276-0FAF-4787-BC69-4F7CC8F4D44A
392 2017-10-25 00:00:00.000 1890 102CA803-8EA7-48CB-95AE-AA2F8F686715
if i use
select top 80... WHERE datumsdifferenz > 0.0
also everything will works fine.
if i use
select top 90... WHERE datumsdifferenz > 0.0
it will throw again these strange error.
if i use a temp table and make the comparision after that it will work?!

i found the solution for myself. the query optimizier makes at first a whole table scan with convert, if i use where clause.
the subquery with FLDART_DSN = (SELECT TOP 1 DSN FROM FLDART WHERE FLDART.Kürzel = 'bezahlt am') will be processed after the outer where clause. the whole table will be scanned in that situation. but the table contains data that are no date values. and that is the reason why the conversion will fail in that situation with outer where clause.
Solution:
SELECT * FROM (SELECT case When von_num < 90000.0 AND von_num > -400000.0 then convert(datetime,VON_NUM,104) END as bezahlt_am, * FROM (SELECT * FROM _TEST WHERE FLDART_DSN = (SELECT TOP 1 DSN FROM FLDART WHERE FLDART.Kürzel = 'bezahlt am')) as test) as test WHERE FLDART_DSN = (SELECT TOP 1 DSN FROM FLDART WHERE FLDART.Kürzel = 'bezahlt am') AND bezahlt_am > GETDATE() - 100.0
SELECT convert(datetime,90000.0,104) -- 2146-05-31 00:00:00.000
SELECT convert(datetime,-40000.0,104) -- 1790-06-26 00:00:00.000

Related

How to find nearest entries before and after a value in Postresql

Similar to this question: How to find the first nearest value up and down in SQL?
I have a Postgres DB Table named prices structured as:
id
column_1
column_2
column_3
date_col
1
1.5
1.7
1.6
1234560000
2
0.9
1.1
1.0
1234570000
3
11.5
23.5
17.5
1234580000
4
8.3
12.3
10.3
1234600000
I'm trying to select either the row that matches exactly to an input date:
Example #1: input query for date_col = 1234580000 would return...
id
column_1
column_2
column_3
date_col
1
1.5
1.7
1.6
1234580000
or if that date does not exist, then retrieve the entries immediately before and after:
Example #2: input query for date_col = 1234590000 would return...
id
column_1
column_2
column_3
date_col
3
11.5
23.5
17.5
1234580000
4
8.3
12.3
10.3
1234600000
I attempted to mess around with the code from the similar question, but I am a bit stuck and have resorted to trying to get the original query date -> check if the DB returned anything in Python, then I create a broad range for the DB to return, send the second query, and then iterate over the returned result in Python. If the result still doesn't exist then I make the range larger... which I know is the wrong way, but my brain is too smooth for this haha
Current code that works when the entry does exist, but does not work when the entry does not exist:
SELECT *
FROM prices, (SELECT id, next_val, last_val
FROM (SELECT t.*,
LEAD(t.id, 1) OVER (ORDER BY t.date_col) as next_val,
LAG(t.id, 1) OVER (ORDER BY t.date_col) as last_val
FROM prices AS t) AS s
WHERE 1234580000 IN (s.date_col, s.next_val, s.last_val)) AS x
WHERE prices.id = x.id OR prices.id = x.next_val OR prices.id = x.last_val
based on the accepted answer this worked like a charm:
SELECT * FROM (SELECT * FROM prices WHERE prices.date_col <= 1234580000 ORDER BY prices.date_col DESC LIMIT 1) AS a UNION (SELECT * FROM prices WHERE prices.date_col >= 1234580000 ORDER BY prices.date_col ASC LIMIT 1)
I guess the simplest solution would be a UNION:
SELECT *
FROM prices
WHERE date_col <= 1234580000
ORDER BY date_col DESC
LIMIT 1
UNION
SELECT *
FROM prices
WHERE date_col >= 1234580000
ORDER BY date_col ASC
LIMIT 1

How to repeat some data points in query results?

I am trying to get the max date by account from 3 different tables and view those dates side by side. I created a separate query for each table, merged the results with UNION ALL, and then wrapped all that in a PIVOT.
The first 2 sections in the link/pic below show what I have been able to accomplish and the 3rd section is what I would like to do.
Query results by step
How can I get the results from 2 of the tables to repeat? Is that possible?
--define var_ent_type = 'ACOM'
--define var_ent_id = '52766'
--define var_dict_id = 113
SELECT
*
FROM
(
SELECT
E.ENTITY_TYPE,
E.ENTITY_ID,
'PERF_SUMMARY' as "TableName",
PS.DICTIONARY_ID,
to_char(MAX(PS.END_EFFECTIVE_DATE), 'YYYY-MM-DD') as "MaxDate"
FROM
RULESDBO.ENTITY E
INNER JOIN PERFORMDBO.PERF_SUMMARY PS ON (PS.ENTITY_ID = E.ENTITY_ID)
WHERE
1=1
-- AND E.ENTITY_TYPE = '&var_ent_type'
-- AND E.ENTITY_ID = '&var_ent_id'
AND PS.DICTIONARY_ID >= 100
AND (E.ACTIVE_STATUS <> 'N' )--and E.TERMINATION_DATE is null )
GROUP BY
E.ENTITY_TYPE,
E.ENTITY_ID,
'PERF_SUMMARY',
PS.DICTIONARY_ID
union all
SELECT
E.ENTITY_TYPE,
E.ENTITY_ID,
'POSITION' as "TableName",
0 as DICTIONARY_ID,
to_char(MAX(H.EFFECTIVE_DATE), 'YYYY-MM-DD') as "MaxDate"
FROM
RULESDBO.ENTITY E
INNER JOIN HOLDINGDBO.POSITION H ON (H.ENTITY_ID = E.ENTITY_ID)
WHERE
1=1
-- AND E.ENTITY_TYPE = '&var_ent_type'
-- AND E.ENTITY_ID = '&var_ent_id'
AND (E.ACTIVE_STATUS <> 'N' )--and E.TERMINATION_DATE is null )
GROUP BY
E.ENTITY_TYPE,
E.ENTITY_ID,
'POSITION',
1
union all
SELECT
E.ENTITY_TYPE,
E.ENTITY_ID,
'CASH_ACTIVITY' as "TableName",
0 as DICTIONARY_ID,
to_char(MAX(C.EFFECTIVE_DATE), 'YYYY-MM-DD') as "MaxDate"
FROM
RULESDBO.ENTITY E
INNER JOIN CASHDBO.CASH_ACTIVITY C ON (C.ENTITY_ID = E.ENTITY_ID)
WHERE
1=1
-- AND E.ENTITY_TYPE = '&var_ent_type'
-- AND E.ENTITY_ID = '&var_ent_id'
AND (E.ACTIVE_STATUS <> 'N' )--and E.TERMINATION_DATE is null )
GROUP BY
E.ENTITY_TYPE,
E.ENTITY_ID,
'CASH_ACTIVITY',
1
--ORDER BY
-- 2,3, 4
)
PIVOT
(
MAX("MaxDate")
FOR "TableName"
IN ('CASH_ACTIVITY', 'PERF_SUMMARY','POSITION')
)
Everything is possible. You only need a window function to make the value repeat across rows w/o data.
--Assuming current query is QC
With QC as (
...
)
select code, account, grouping,
--cash,
first_value(cash) over (partition by code, account order by grouping asc rows unbounded preceding) as cash_repeat,
perf,
--pos,
first_value(pos) over (partition by code, account order by grouping asc rows unbounded preceding) as pos_repeat
from QC
;
See first_value() help here: https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/FIRST_VALUE.html#GUID-D454EC3F-370C-4C64-9B11-33FCB10D95EC

Postgres Query Return 0 If Empty For Date

I did some digging within stackoverflow for a solution and nothing quite matched what I was after (I don't think).
My goal is to combine two queries that will return 3 columns [date, OUT, IN] as output.
On their own, the two queries return the correct output.
1st SELECT query for OUT returns
[03/01, 4],[03/02, 10],[03/03, 21],[03/01, 4]
2nd SELECT query for IN returns
[03/01, 4],[03/03, 25]
Using WITH to combine the two queries, two different ways, the outputs are identical.
[03/01, 4, 4], [03/03, 21, 25] -> OUT as inner query
[03/01, 4, 4], [03/03, 21, 25] -> IN as inner query
As can be seen, because nothing came IN on the 2nd and 4th of March, an empty row is returned for those dates. I believe the correct way to use the WITH query is to have the OUT as the inner query so the 2nd and 4th are part of the recursion in the outer query.
Pasted below is a simplified version of what the WITH query looks like. On its own, the inner query returns a count for each day of March. For some reason, the outer query does not return 0 when it does not have a count for the date in question (2nd and 4th).
Anyone have advice on how to solve this one? Could I be using COALESCE incorrectly?
WITH first_query (the_date, out) AS
(
SELECT DISTINCT ON(to_char(t1.date, 'dd')) to_char(t1.out_date, 'dd') AS out_date2, count(to_char(t1.out_date, 'dd')) AS out
FROM table_out AS t1
WHERE to_char(t1.out_date,'yyyy') = to_char(date_trunc('year', CURRENT_DATE), 'YYYY')
AND to_char(t1.out_date,'MM') = to_char(date_trunc('month', CURRENT_DATE), 'MM')
AND t1.out_id = 14
GROUP BY out_date2
)
SELECT fq.the_date AS day_of_month, fq.out, COALESCE( ( SELECT COUNT(to_char(t1.in_date, 'DD')) ), 0) AS in
FROM first_query fq, table_in t1
WHERE to_char(t1.in_date,'yyyy') = to_char(date_trunc('year', CURRENT_DATE), 'YYYY')
AND to_char(t1.in_date,'MM') = to_char(date_trunc('month', CURRENT_DATE), 'MM')
AND to_char(t1.in_date,'dd') = fq.the_date
GROUP BY day_of_month, fq.out
ORDER BY day_of_month ASC
The answer ended up requiring CAST and LEFT JOIN as Jeremy suggested. The timestamps made this harder than it had to be.
SELECT CAST(t1.out_date AS Date) AS date2, count(DISTINCT
t1.id) out, count(DISTINCT t2.id) AS in
FROM table_out as t1
LEFT JOIN table_in AS t2 ON CAST(t1.out_date AS Date) =
CAST(t2.in_date AS Date)
WHERE to_char(t1.out_date,'yyyy') =
to_char(date_trunc('year', CURRENT_DATE), 'YYYY')
AND to_char(t1.out_date,'MM') = to_char(date_trunc('month',
CURRENT_DATE), 'MM')
AND t1.out_id = 14
GROUP BY out_date2
The answer ended up requiring CAST and LEFT JOIN as Jeremy suggested. The timestamps made this harder than it had to be.
SELECT CAST(t1.out_date AS Date) AS date2, count(DISTINCT
t1.id) out, count(DISTINCT t2.id) AS in
FROM table_out as t1
LEFT JOIN table_in AS t2 ON CAST(t1.out_date AS Date) =
CAST(t2.in_date AS Date)
WHERE to_char(t1.out_date,'yyyy') =
to_char(date_trunc('year', CURRENT_DATE), 'YYYY')
AND to_char(t1.out_date,'MM') = to_char(date_trunc('month',
CURRENT_DATE), 'MM')
AND t1.out_id = 14
GROUP BY out_date2

sorting DDMMYYYY monthly using postgreSQL

I am writing a query using PostgreSQL to count something but I want to sort the date (DDMMYYYY) properly.
With this following codes,
WITH dis_id AS (SELECT
DISTINCT ON (source_user_id) source_user_id,
created_at
FROM public.info_scammers )
SELECT d.date, count(dis_id.source_user_id)
FROM (SELECT to_char(date_trunc('day',(current_date - offs)), 'DD-MM-YYYY') AS date
FROM generate_series(0,365,1) AS offs
) d LEFT OUTER JOIN
dis_id
ON (d.date = to_char(date_trunc('day',dis_id.created_at),'YYYY-MM-DD'))
GROUP BY d.date
The result is
Date | Count
01-01-2017 | 0
01-02-2017 | 0
01-03-2017 | 0
What I want is
Date | Count
01-01-2017 | 0
02-01-2017 | 0
03-01-2017 | 0
I have looked up the existing problems. But most of them do not use PostgreSQL
Thank you
Leave d.date as type date in the inner SELECT (don't convert it to text with to_char), then add ORDER BY d.date and do the conversion to text in the outer SELECT.
Something like:
WITH dis_id AS (...)
SELECT to_char(d.date, 'DD-MM-YYYY'), count(...)
FROM (SELECT date_trunc(...) AS date
FROM ...
) d
LEFT OUTER JOIN ...
GROUP BY to_char(d.date, 'DD-MM-YYYY')
ORDER BY d.date;

How to display rollup data in new column?

I have the following query which returns the number of android questions per each day on StackOverflow in the year of 2011. I want to get the sum of all the questions asked during the year 2011. For this I am using ROLLUP.
select
year(p.CreationDate) as [Year],
month(p.CreationDate) as [Month],
day(p.CreationDate) as [Day],
count(*) as [QuestionsAskedToday]
from Posts p
inner join PostTags pt on p.id = pt.postid
inner join Tags t on t.id = pt.tagid
where
t.tagname = 'android' and
p.CreationDate > '2011-01-01 00:00:00'
group by year(p.CreationDate), month(p.CreationDate),day(p.CreationDate)
​with rollup
order by year(p.CreationDate), month(p.CreationDate) desc,day(p.CreationDate) desc​
This is the output:
The sum of all questions asked on each day in 2011 is being displayed in the QuestionsAskedToday column itself.
Is there a way to display the rollup in a new column with an alias?
Link to the query
To show this as a column rather than a row you can use SUM(COUNT(*)) OVER () instead of ROLLUP. (Online Demo)
SELECT YEAR(p.CreationDate) AS [Year],
MONTH(p.CreationDate) AS [Month],
DAY(p.CreationDate) AS [Day],
COUNT(*) AS [QuestionsAskedToday],
SUM(COUNT(*)) OVER () AS [Total]
FROM Posts p
INNER JOIN PostTags pt
ON p.id = pt.postid
INNER JOIN Tags t
ON t.id = pt.tagid
WHERE t.tagname = 'android'
AND p.CreationDate > '2011-01-01 00:00:00'
GROUP BY YEAR(p.CreationDate),
MONTH(p.CreationDate),
DAY(p.CreationDate)
ORDER BY YEAR(p.CreationDate),
MONTH(p.CreationDate) DESC,
DAY(p.CreationDate) DESC
You could take an approach like this: Example
SELECT
YEAR(p.CreationDate) AS 'Year'
, CASE
WHEN GROUPING(MONTH(p.CreationDate)) = 0
THEN CAST(MONTH(p.CreationDate) AS VARCHAR(2))
ELSE 'Totals:'
END AS 'Month'
, CASE
WHEN GROUPING(DAY(p.CreationDate)) = 0
THEN CAST(DAY(p.CreationDate) AS VARCHAR(2))
ELSE 'Totals:'
END AS [DAY]
, CASE
WHEN GROUPING(MONTH(p.CreationDate)) = 0
AND GROUPING(DAY(p.CreationDate)) = 0
THEN COUNT(1)
END AS 'QuestionsAskedToday'
, CASE
WHEN GROUPING(MONTH(p.CreationDate)) = 1
OR GROUPING(DAY(p.CreationDate)) = 1
THEN COUNT(1)
END AS 'Totals'
FROM Posts AS p
INNER JOIN PostTags AS pt ON p.id = pt.postid
INNER JOIN Tags AS t ON t.id = pt.tagid
WHERE t.tagname = 'android'
AND p.CreationDate >= '2011-01-01'
GROUP BY ROLLUP(YEAR(p.CreationDate)
, MONTH(p.CreationDate)
, DAY(p.CreationDate))
ORDER BY YEAR(p.CreationDate)
, MONTH(p.CreationDate) DESC
, DAY(p.CreationDate) DESC​​​​​​​
If this is what you wanted, the same technique can be applied to Years as well to total them in the new column, or their own column, if you want to query for multiple years and aggregate them.