My SUBSTRING/CHARINDEX query broke a few nights ago and I'm struggling to understand why.
I'm trying to select the characters (Date) between "Appt. Date:" and "Appt. Time:" in comments like:
Status: Future Appointment Appt. Date:12/14/18 Appt. Time:9:30am
Status: Obtaining Results Appt. Date:10/05/18 Appt. Time:4:00
Status: Appt. Date:8/28/2018 Appt. Time: 9:15am
Using this query:
select ltrim(
rtrim(
SUBSTRING([Order Comments],
CHARINDEX('Appt. Date:', [Order Comments]) + LEN('Appt. Date:'),
CHARINDEX('Appt. Time',[Order Comments], CHARINDEX('Appt. Date:', [Order Comments])) -
(CHARINDEX('Appt. Date:', [Order Comments]) + LEN('Appt. Date:'))
)
)
) as 'OrderApptDate'
FROM [HIRS_Tools].[dbo].[OMT_BOD]
WHERE [Order Comments] like 'status:%'
SQL error:
Invalid Length Parameter passed to the LEFT or SUBSTRING function.
Should I be taking a different approach to selecting the date characters, or is there simply a problem with my current query? Any help would be greatly appreciated.
First, many thanks for the posts everyone. Even though none of them gave me the exact answer, they all gave me a piece to answer the puzzle. Especially, HABO and 3N1GM4!
Eventually I discovered the Appt Date and Appt Time fields are not always exactly the same. There are combinations of Appt. Date: & Appt. Time, Appt. Date - & Appt. Time, Appt Date & Appt. Time...All discovered from HABO's field length expression.
So, I added a case statement adjusting for each [Appt Date/Appt Time] combo field length. This resolved everything and gave me enough leverage to have my users go clean up the remaining records and implement a better data entry work flow.
The new query looks like this:
select
case
when CHARINDEX('Appt. Time',[Order Comments], CHARINDEX('Appt. Date:', [Order Comments])) - (CHARINDEX('Appt. Date:', [Order Comments]) + LEN('Appt. Date:')) <15
and [Type]='Procedure' AND [Order Comments] like 'status:%' and [Order Comments] like '%Appt. Time%'
then ltrim(rtrim(SUBSTRING([Order Comments], CHARINDEX('Appt. Date:', [Order Comments]) + LEN('Appt. Date:'), CHARINDEX('Appt. Time:',[Order Comments], CHARINDEX('Appt. Date:', [Order Comments])) - (CHARINDEX('Appt. Date:', [Order Comments]) + LEN('Appt. Date:')))))
when CHARINDEX('Appt. Time',[Order Comments], CHARINDEX('Appt. Date:', [Order Comments])) - (CHARINDEX('Appt. Date:', [Order Comments]) + LEN('Appt. Date:')) <15
and [Type]='Procedure' AND [Order Comments] like 'status:%' and [Order Comments] like '%Appt Time%'
then ltrim(rtrim(SUBSTRING([Order Comments], CHARINDEX('Appt Date:', [Order Comments]) + LEN('Appt Date:'), CHARINDEX('Appt Time:',[Order Comments], CHARINDEX('Appt Date:', [Order Comments])) - (CHARINDEX('Appt Date:', [Order Comments]) + LEN('Appt Date:')))))
when (CHARINDEX('Appt. Time',[Order Comments], CHARINDEX('Appt. Date -', [Order Comments])) - (CHARINDEX('Appt. Date -', [Order Comments]) + LEN('Appt. Date -'))) <15
and [Type]='Procedure' AND [Order Comments] like 'status:%' and [Order Comments] like '%Appt. Date -%'
then ltrim(rtrim(SUBSTRING([Order Comments], CHARINDEX('Appt. Date -', [Order Comments]) + LEN('Appt. Date -'), CHARINDEX('Appt. Time:',[Order Comments], CHARINDEX('Appt. Date -', [Order Comments])) - (CHARINDEX('Appt. Date -', [Order Comments]) + LEN('Appt. Date -')))))
when (CHARINDEX('Appt. Time',[Order Comments], CHARINDEX('Appt. Date ', [Order Comments])) - (CHARINDEX('Appt. Date ', [Order Comments]) + LEN('Appt. Date '))) <15
and [Type]='Procedure' AND [Order Comments] like 'status:%' and [Order Comments] like '%Appt. Date %'
then ltrim(rtrim(SUBSTRING([Order Comments], CHARINDEX('Appt. Date ', [Order Comments]) + LEN('Appt. Date '), CHARINDEX('Appt. Time:',[Order Comments], CHARINDEX('Appt. Date ', [Order Comments])) - (CHARINDEX('Appt. Date ', [Order Comments]) + LEN('Appt. Date ')))))
else [Date - Due] end as 'OrderApptDate'
FROM [HIRS_Tools].[dbo].[OMT_BOD]
WHERE ([Order Comments] like 'status:%')
Again, thank you for the help and I hope someone finds this useful in the future.
Related
I'm working on the Accrual Reversal query in PostgreSQL. The system running doesn't have the reversal flag. So I need to consider all the end of the day of previous month accrued invoices as the reversal amount. And need to union them all with the main query. I can do it for last month but invoice date are dynamic, user may give 2 years as invoice period. For those 2 years, all the previous month data should be considered as accrued reversal. Here is the query
select invoicename, * from accountpay where invoice_date between '2020-01-01' and '2021-12-31'
union all
select concat('Accured Reversal', invoicename) as reference, * from accountpay where accrual = true and invoice_date::date = (select concat(date_part('year',((('2021-12-30'::date) - interval '1 month'))), '-', date_part('month',((('2021-12-30'::date) - interval '1 month'))), '-01')::date + interval '1 month' - interval '1 day')
Please help me to do this.
Thanks in Advance
SELECT (
Date_trunc('MONTH',a) + interval '1 month -1 day ')
as last_day_of_month
FROM generate_series(
'2020-01-01 00:00'::timestamp
- interval '12 months',
'2022-01-01 00:00',
'1 month') as dt(a);
get last_day_of_month from '2020-01-01 00:00' till '2022-01-01 00:00'
Then your sql would be
invoice_date in
(SELECT (Date_trunc('MONTH',a) + interval '1 month -1 day ')
as last_day_of_month
FROM generate_series(
'2020-01-01 00:00'::timestamp
- interval '12 months',
'2021-01-01 00:00',
'1 month') as dt(a))
This will get the last day of last 12 months so the number of months will be place holder (dynamic) and all the last day of the months will be in IN clause.
SQL re-written:
WITH date_cte AS
(
SELECT Date_trunc('MONTH',dt)+ interval '1 month -1 day ' last_day_of_month
FROM generate_series('2021-11-30 00:00:00'::timestamp - interval '12 months','2021-11-30 00:00:00','1 month') t(dt))
select invoicename, * from accountpay where invoice_date between '2020-01-01' and '2021-12-31'
union all
select concat('Accured Reversal', invoicename) as reference, * from accountpay where accrual = true and invoice_date::date in (select * from date_cte);
Basically , the last dates are generated this way for 12 months:
WITH date_cte AS
(
SELECT Date_trunc('MONTH',dt)+ interval '1 month -1 day ' last_day_of_month
FROM generate_series('2021-11-30 00:00:00'::timestamp - interval '12 months','2021-11-30 00:00:00','1 month') t(dt))
SELECT *
FROM date_cte;
last_day_of_month
---------------------
2020-11-30 00:00:00
2020-12-31 00:00:00
2021-01-31 00:00:00
2021-02-28 00:00:00
2021-03-31 00:00:00
2021-04-30 00:00:00
2021-05-31 00:00:00
2021-06-30 00:00:00
2021-07-31 00:00:00
2021-08-31 00:00:00
2021-09-30 00:00:00
2021-10-31 00:00:00
2021-11-30 00:00:00
You can replace 12 months by any number of months or you can make it year too like:
...generate_series('2021-11-30 00:00:00'::timestamp - interval '1 year','2021-11-30 00:00:00','1 month')
I have a code here where it groups all data according to their week starting day, such as Sunday, Monday, and Tuesday. Code works on Monday and Sunday but on Tuesday, as you can see, Oct 12 is grouped on Tuesday the following week.
Code:
select case when day.week_starting = 'Sunday' then date_trunc('week', table1.date::date + 1)::date - 1
when day.week_starting = 'Tuesday' then date_trunc('week', table1.date::date + 2)::date + 1
when day.week_starting = 'Monday' then date_trunc('week', table1.date::date)::date
end as local_date_created, date
Update: I found the workaround. Here:
case when day.week_starting = 'Sunday' then date_trunc('week', table1.date::date + 1)::date - 1
when day.week_starting = 'Tuesday' then date_trunc('week', table1.date::date)::date + 1
when day.week_starting = 'Monday' then date_trunc('week', table1.date::date)::date
end as local_date_created,date
I am trying to find basket items (category, subcategory) in last 3 orders of each customer. So, in the end, I am hoping to cluster customers according to items or categories that were mostly bought in last 3 orders.I am stuck on finding a solution to calculate last 3 orders of each customer. I should use LOD expressions but which one and how?
I think using Fixed [Client id] is the starting point. Should I rank orders descending ( based on order date) and then filter it with "<=3".
Trying replicating your problem on sample superstore data.
creating this calculation will give you last order for each customer
{fixed [Customer Name]: max([Order Date])} = [Order Date]
creating this calculation will give you last 2 orders
{Fixed [Customer Name]:MAX(
If [Order Date] <> {fixed [Customer Name]: max([Order Date])}
then [Order Date] END
)} = [Order Date]
OR
{fixed [Customer Name]: max([Order Date])} = [Order Date]
similarly creating this calculation will give you last 3 orders
{ FIXED [Customer Name] : max( IF
{Fixed [Customer Name]:MAX(
If [Order Date] <> {fixed [Customer Name]: max([Order Date])}
then [Order Date] END
)} <> [Order Date]
AND
{fixed [Customer Name]: max([Order Date])} <> [Order Date]
THEN [Order Date] END)} = [Order Date]
OR
{Fixed [Customer Name]:MAX(
If [Order Date] <> {fixed [Customer Name]: max([Order Date])}
then [Order Date] END
)} = [Order Date]
OR
{fixed [Customer Name]: max([Order Date])} = [Order Date]
only assumption is that there aren't more than 1 order on any given date.
check it
Good afternoon everyone,
I have written a Stored Procedure that as it is works and executes relatively fast as it is just doing fairly simple calculation. I guess you could say my issue with the procedure itself is the number of repeated 'CASE Statement' both in the SELECT and ORDER BY clause. My TSQL knowledge is still fairly N00bish as I am still a 'P' plate at best. Is it possible to streamline my code further so that I only have the CASE WHEN calculation appearing once and I can continue to utilize it in multiple places? I believe this will be better for future proofing as well as I only need to make the changes at the root statement without having to change it in multiple locations!
#Officer_Name is a variable passed in from the user interface. As you can see both the F_YEAR (Fiscal Year), F_Quarter (Fiscal Quarter) Field calculation is repeated again in the Order By part of the statement and I am wondering if that can be avoided :) Many many thanks in advance for rescuing this damoiseau in distress, I hope there is a generous expert with a greater level of TSQL out there who can do me this favor! Much appreciated.
BEGIN
SELECT TOP (100) PERCENT
COUNT(DISTINCT(dbo.TableA.[Account ID])) AS Applications,
SUM(CASE WHEN [Client Claims] LIKE '%claim%' THEN 1 ELSE 0 END) AS Main_Client,
COUNT([TableA_ID]) AS Clients,
(CASE
WHEN [Finalised date] < '07/01/' + CONVERT(VARCHAR(4), DATEPART(Year,GETDATE()) - 2) THEN 'PAST CASES'
WHEN [Finalised date] BETWEEN '07/01/' + CONVERT(VARCHAR(4), DATEPART(Year,GETDATE()) - 2) AND '06/30/' + CONVERT(VARCHAR(4), DATEPART(Year,GETDATE()) - 1) THEN 'YEAR'
WHEN MONTH([Finalised date]) BETWEEN 1 AND 3 THEN ' Q3'
WHEN MONTH([Finalised date]) BETWEEN 4 AND 6 THEN ' Q4'
WHEN MONTH([Finalised date]) BETWEEN 7 AND 9 THEN ' Q1'
WHEN MONTH([Finalised date]) BETWEEN 10 AND 12 THEN ' Q2'
END) AS F_Quarter,
(CASE
WHEN [Finalised date] < '07/01/' + CONVERT(VARCHAR(4), DATEPART(Year,GETDATE()) - 2) THEN CONVERT(VARCHAR(4), DATEPART(Year,GETDATE()) - 2) + ' & Older'
WHEN MONTH([Finalised date]) BETWEEN 1 AND 6 THEN convert(char(4), YEAR([Finalised date]) - 0)
WHEN MONTH([Finalised date]) BETWEEN 7 AND 12 THEN convert(char(4), YEAR([Finalised date]) + 1)
ELSE convert(char(4), YEAR([Finalised date]))
END) AS F_YEAR
FROM dbo.TableB INNER JOIN
dbo.TableA ON dbo.TableB.[Account ID] = dbo.TableA.[Account ID] LEFT OUTER JOIN
dbo.Officers ON dbo.TableA.[Account Officer] = dbo.Officers.FullName
WHERE [Case Officer] = #Officer_Name AND [Finalisation] IS NOT NULL
GROUP BY
(CASE
WHEN [Finalised date] < '07/01/' + CONVERT(VARCHAR(4), DATEPART(Year,GETDATE()) - 2) THEN CONVERT(VARCHAR(4), DATEPART(Year,GETDATE()) - 2) + ' & Older'
WHEN MONTH([Finalised date]) BETWEEN 1 AND 6 THEN convert(char(4), YEAR([Finalised date]) - 0)
WHEN MONTH([Finalised date]) BETWEEN 7 AND 12 THEN convert(char(4), YEAR([Finalised date]) + 1)
ELSE convert(char(4), YEAR([Finalised date]))
END),
(CASE
WHEN [Finalised date] < '07/01/' + CONVERT(VARCHAR(4), DATEPART(Year,GETDATE()) - 2) THEN 'PAST CASES'
WHEN [Finalised date] BETWEEN '07/01/' + CONVERT(VARCHAR(4), DATEPART(Year,GETDATE()) - 2) AND '06/30/' + CONVERT(VARCHAR(4), DATEPART(Year,GETDATE()) - 1) THEN 'YEAR'
WHEN MONTH([Finalised date]) BETWEEN 1 AND 3 THEN ' Q3'
WHEN MONTH([Finalised date]) BETWEEN 4 AND 6 THEN ' Q4'
WHEN MONTH([Finalised date]) BETWEEN 7 AND 9 THEN ' Q1'
WHEN MONTH([Finalised date]) BETWEEN 10 AND 12 THEN ' Q2'
END)
ORDER BY F_YEAR DESC, F_Quarter
END
You can put your CASE expression in a CTE and refer to it multiple times by its alias in the query to follow. However, since your CASE expression for F_YEAR is different from the one for F_Quarter there is no way to only use one CASE expression for the entire query. In pseudo-code, you can do this:
WITH cte AS (
SELECT ...
, {CASE Expression for year} AS F_Year
, {CASE Expression for quarter} AS F_Quarter
FROM...
)
SELECT ... F_Year, F_Quarter
FROM ... WHERE ...
GROUP BY F_Year, F_Quarter ...
can anyone tell me the logic of getting last year's last 2 months records from the next year Jan or feb ?. For eg: I want to compare the sales from Jan 2016 as current month and Dec 2015 as previous month and Nov 2015 as two months before. I tried like this, but not working if it is for Jan or feb
(case when extract(month from m.validfrom) = extract(month from current_date)-1 then 'Previous Month'
when extract(month from m.validfrom) = extract(month from current_date)-2 then 'Two Months Before'
when extract(month from m.validfrom) = extract(month from current_date) then 'Current Month' end ) as month,
date_trunc
case date_trunc('month', m.validfrom)
when date_trunc('month', current_date - interval '1 month') then 'Previous Month'
when date_trunc('month', current_date - interval '2 month') then 'Two Months Before'
when date_trunc('month', current_date) then 'Current Month'
end as month