SQL to bucket numbers - sql

so i am trying to bucket business days into buckets but I am getting stuck:
IF OBJECT_ID('mattexcel2') IS NOT NULL DROP TABLE mattexcel2 PRINT ' DROP TEMP TABLE'
SELECT * INTO mattexcel2 FROM SA_MASTER_BASE PRINT ' INSERT INTO TEMP TABLE'
GO
ALTER TABLE mattexcel2 ADD [Bucket] NVARCHAR(255) PRINT 'Bucket'
GO
UPDATE mattexcel2 SET [Bucket] = '0-3 Days' where [Business days in current Status] <= 3
GO
When I run this in SQL I get:
Conversion failed when converting the nvarchar value '1.91' to data
type int.
So I want 1.9 to fall under my Bucket column as 0-3 Days.

As mentioned in the comments, the problem is:
[Business days in current Status] <= 3
Because [Business days in current Status] is a string, it needs to be converted to a number for the comparison. SQL Server chooses an integer. You can fix this by using a decimal place:
UPDATE mattexcel2
SET [Bucket] = '0-3 Days'
WHERE try_convert(decimal(38, 4), [Business days in current Status]) <= 3.0;
However, I would suggest that you use a computed column:
alter table mattexcel2
add bd_bucket as (case when try_convert(decimal(38, 4), [Business days in current Status]) <= 3.0
then '0-3 Days'
end);
A computed column will always have the right value, without being updated.

Related

How to reset SQL Server Inbuilt Function (DAY)

I wanted to update my column in the table called [DAY] and didn't realize that there was also an inbuilt function DAY. So I accidentally updated the DAY value instead causing it to show 'N.A' which is the value I've set.
I've tried searching on google but no matches appear, tried finding the inbuilt functions on SQL Server but to no avail.
The update code was this:
UPDATE v_ClassroomOccupancy SET
[DAY] = (CASE WHEN (LocalTime NOT BETWEEN CLASSSTARTDATE AND CLASSENDDATE) THEN 'N.A' ELSE DAY END)
Any way I can reset or set DAY inbuilt function to the default value?
DAY is reserved keyword in SQL Server, since you already named your column as DAY, make sure to enclosed this to brackets [] each time you are using this column.
UPDATE v_ClassroomOccupancy SET
[DAY]=(CASE WHEN(LocalTime NOT BETWEEN CLASSSTARTDATE AND CLASSENDDATE) THEN 'N.A' ELSE [DAY] END)
To reset your [DAY] Column, use getdate() function to get your current datetime
UPDATE v_ClassroomOccupancy SET
[DAY]=(CASE WHEN GETDATE() NOT BETWEEN CLASSSTARTDATE AND CLASSENDDATE THEN 'N.A' ELSE datename(dw, GETDATE()) END)

SQL Server Arithmetic Overflow Error - converting nvarchar to datetime

I'm retrieving data from a view, and one of the columns I'm using is nvarchar(50), but is only ever N'True' or N'False', depending on the operation of a related date column in this parent view.
The following code retrieves the record ID and the column I'm looking for, YTD:
SELECT Enquiry_Number, YTD
FROM dbo.vw_SalesPO_YTD
Output:
ENQ-001 True
ENQ-002 False
ENQ-003 True
However, I'm unable to filter my results using this YTD column for some reason. If I attempt to do this:
SELECT Enquiry_Number, YTD
FROM dbo.vw_SalesPO_YTD
WHERE YTD = N'True'
Then it fails with the following error:
Arithmetic overflow error converting expression to data type datetime.
Which I don't understand because there are no datetime expressions in play in this query. Yes the True or False was determined by comparing datetimes in the parent view, but I don't understand how that might have trickled down to this subquery. Attempting the same thing in the parent view yields the same error - I'm demonstrating it this way for simplicity's sake.
However, performing a similar operation in the SELECT portion of the query works without issues:
SELECT
Enquiry_Number,
YTD,
CASE
WHEN YTD = N'True' THEN 1 ELSE 0
END As C
FROM
dbo.vw_SalesPO_YTD
Output:
ENQ-001 True 1
ENQ-002 False 0
ENQ-003 True 1
However, these 1's and 0's inherit the same flaw, where I can't use them in a WHERE clause without getting this datetime error.
I've been searching hard and am not sure how to identify the core issue. I've been reading things about Collations and type precedence, but can't understand why this behaviour is happening.
When I've checked YTD in INFORMATION_SCHEMA.COLUMNS, it confirms that this column is no different from other columns in my table: YTD is nvarchar(50), using the Latin1_General_CI_AS collation.
Related question: SQL Server Arithmetic overflow error converting expression to data type datetime
The Source of the Problem
This issue is still unsolved, but if you wish to reproduce it, this code from the parent view must be generating this issue:
CASE WHEN
Award_Date <= DATEFROMPARTS(FinancialYear - 1, 11, 1) + GETDATE() - DATEADD(year, DATEDIFF(month, '20161101', GETDATE()) / 12, '20161101')
THEN N'True'
ELSE N'False'
END
Yes this looks overly complicated. We're checking the Award_Date against its associated FinancialYear, which runs from November 1st to October 31st. Each record already knows which FinancialYear it's in. The ultimate aim is to compare TODAY's position (2016-11-30) against TODAY last year (2015-11-30), and TODAY the year before (2014-11-30), etc.
So the code takes today's date and combines it with the FinancialYear for the associated record, and spits out whether the record had occurred between the start of its financial year and the today of the same year. And it's doing this successfully, but then I can't do anything with the N'True' or N'False' it's producing.
I do not know what the type of the source YTD is.
Try using the following:
SELECT Enquiry_Number, [YTD] FROM (
SELECT Enquiry_Number, CONVERT(nvarchar(10),YTD) AS [YTD] FROM dbo.vw_SalesPO_YTD
) AS A
WHERE A.YTD = N'True'
10 is just a thump suck value. It will cut of any part of the field longer that 10. It depends on your actual field size.
SELECT *
FROM (SELECT Enquiry_Number, YTD
FROM dbo.vw_SalesPO_YTD) AS A
WHERE cast(A.YTD as varchar) = 'True'
I used the following as an example:
DECLARE #Data TABLE
(
Enquiry_Number nvarchar(10),
YTD nvarchar(50)
)
INSERT INTO #Data(Enquiry_Number, YTD)
SELECT N'ENQ-001', N'True' UNION
SELECT N'ENQ-002', N'False' UNION
SELECT N'ENQ-003', N'True'
SELECT Enquiry_Number, [YTD] FROM (
SELECT Enquiry_Number, CONVERT(nvarchar(10),YTD) AS [YTD] FROM #Data
) AS A
WHERE A.YTD = N'True'
Result:
ENQ-001 True
ENQ-003 True
There must be results in the YTD field that causes to return it as a datetime type.
Try a query like:
SELECT * FROM dbo.vw_SalesPO_YTD WHERE ISDATE(YTD)= 1
Updated Question:
Try:
ISNULL(CASE WHEN Award_Date <= DATEFROMPARTS(FinancialYear - 1, 11, 1) + GETDATE() - DATEADD(year, DATEDIFF(month, '20161101', GETDATE()) / 12, '20161101') THEN N'True' ELSE N'False' END, 'false')
Try this:- Don't add n before string
SELECT *
FROM (SELECT Enquiry_Number, YTD
FROM dbo.vw_SalesPO_YTD) AS A
WHERE A.YTD = 'True'
This is a successful workaround, not a solution per se or explanation of the core issue.
There's obviously an issue with the data coming out of the view, which will not allow YTD column's results to be operated on in a WHERE clause, and yet they can be operated on by the time the query reaches its SELECT phase.
I've created a new table which explicitly defines the YTD column as nvarchar(50), and then inserted all the records from my view into this table, which has resolved the issue. The records can then be sorted and filtered by YTD as they are supposed to.

SQL subquery based on row values with unrelated table

I need to get a count of records in an unrelated table, based on the row values in a query with some moderately complex joins. All data is on one server in a single SQL 2012 database, on several different tables.
I am recreating ticket movement history for a single ticket at a time, from audit records and need to calculate business days for the spans in rows created by the joins. Tickets are moved around between areas (ASSIGNMENT), and there are guidelines on how long it should be at any one area. The ticket may go to the same area multiple times with each time restarting the time count.
I need to consider company holidays in the business day calculations. After looking at several solutions for business day calculations on SE I decided to go with a company calendar table (dbo.UPMCCALENDARM1) and count the dates between spans. Seemed like a great idea...
I can't figure out how to use the row values as parameters for the date count query.
The query below has working solutions with a Variable and with a Cross Join, but it only works with hard coded dates, if I try to use the field values it does not work, because they are not part of the sub query and can not be bound.
-- between DV_im_Audit_ASSIGNMENT.Time and Detail.RESOLVED_TIME
In theory I could probably get there using this full query in the sub query to get the date count, but this is as short as I can make it and still get clean data. It is a pretty heavy lift for an on demand report, that would be my last option. So I want to reach out to UPMCCALENDARM1 as each occurrence of DV_im_Audit_ASSIGNMENT.Time and Detail.RESOLVED_TIME are listed.
Can it be done? If so how?
declare #NonBus integer
set #NonBus = '0'
set #NonBus = (select Count(UPMCCALENDARM1.DATE) as NonBus
from dbo.UPMCCALENDARM1
where UPMC_BUSINESS_DAY = 'f'
and UPMCCALENDARM1.DATE
between '2015-08-01' and '2015-08-31'
-- between DV_im_Audit_ASSIGNMENT.Time and Detail.RESOLVED_TIME
)
select DV_im_Audit_ASSIGNMENT.Incident_ID
, DV_im_Audit_ASSIGNMENT.Old_ASSIGNMENT
, DV_im_Audit_ASSIGNMENT.New_ASSIGNMENT
, DV_im_Audit_ASSIGNMENT.Time as Assign_Time
, B.Time as Reassign_Time
, Detail.OPEN_TIME
, Cal.NonBus
, NonBus
, Detail.RESOLVED_TIME
, A.rownumA
, B.rownumB
from dbo.DV_im_Audit_ASSIGNMENT
--Get RownumA as a select join so I can work with it here, else get an invalid column name 'rownumA' error
left join(select Incident_ID
, Old_ASSIGNMENT
, New_ASSIGNMENT
, [Time]
, rownumA = ROW_NUMBER() OVER (ORDER BY DV_im_Audit_ASSIGNMENT.Incident_ID, DV_im_Audit_ASSIGNMENT.Time)
from dbo.DV_im_Audit_ASSIGNMENT
where Incident_ID = ?
) as A
on DV_im_Audit_ASSIGNMENT.Incident_ID = A.Incident_ID
and DV_im_Audit_ASSIGNMENT.New_ASSIGNMENT = A.New_ASSIGNMENT
and DV_im_Audit_ASSIGNMENT.Time = A.Time
--Get time assigned to next group, is problomatic when assigned to the same group multiple times.
left join(select Incident_ID
, Old_ASSIGNMENT
, New_ASSIGNMENT
, [Time]
, rownumB = ROW_NUMBER() OVER (ORDER BY DV_im_Audit_ASSIGNMENT.Incident_ID, DV_im_Audit_ASSIGNMENT.Time)
from dbo.DV_im_Audit_ASSIGNMENT
where Incident_ID = ?
) as B
on DV_im_Audit_ASSIGNMENT.Incident_ID = B.Incident_ID
and DV_im_Audit_ASSIGNMENT.New_ASSIGNMENT = B.Old_ASSIGNMENT
and DV_im_Audit_ASSIGNMENT.Time < B.Time
and rownumA = (B.rownumB - 1)
--Get current ticket info
left join (select Incident_ID
, OPEN_TIME
, RESOLVED_TIME
from dbo.DV_im_PROBSUMMARYM1_Detail
where Incident_ID = ?
) as Detail
on DV_im_Audit_ASSIGNMENT.Incident_ID = Detail.Incident_ID
--Count non-bussiness days. This section is in testing and does not use dataview as a source.
-- this gets the date count for one group of dates, need a different count for each row based on assign time.
cross join (Select Count(UPMCCALENDARM1.DATE) as NonBus
from dbo.UPMCCALENDARM1
where UPMC_BUSINESS_DAY = 'f'
and UPMCCALENDARM1.DATE
between '2015-08-01' and '2015-08-30'
-- between DV_im_Audit_ASSIGNMENT.Time and Detail.RESOLVED_TIME
) as Cal
--Get data for one ticket
where DV_im_Audit_ASSIGNMENT.Incident_ID = ?
ORDER BY DV_im_Audit_ASSIGNMENT.Incident_ID, DV_im_Audit_ASSIGNMENT.Time
Results
FYI - I am running this SQL through BIRT 4.2, I believe there are few SQL items that will not pass through BIRT
Following the suggestion by #Dominique I created a custom scalar function (using the wizard in SSMS), I used default values for the dates as I had started by playing with stored procedure and that made it easier to test. This problem requires a function as it will return a value per row, where a stored procedure will not.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: James Jenkins
-- Create date: September 2015
-- Description: Counts Business Days for UPMC during a span of dates
-- =============================================
CREATE FUNCTION dbo.UPMCBusinessDayCount
(
-- Add the parameters for the function here
#StartDate date = '2015-08-01',
#EndDate date = '2015-08-31'
)
RETURNS int
AS
BEGIN
-- Declare the return variable here
DECLARE #BusDay int
-- Add the T-SQL statements to compute the return value here
SELECT #BusDay = Count(UPMCCALENDARM1.DATE)
from dbo.UPMCCALENDARM1
where UPMC_BUSINESS_DAY = 't'
and UPMCCALENDARM1.DATE between #StartDate and #EndDate
-- Return the result of the function
RETURN #BusDay
END
GO
After the function is created in the database I added these two lines to my select statement, and it works perfectly.
--Custom function counts business days on UPMCCALENDARM1
, dbo.UPMCBusinessDayCount(DV_im_Audit_ASSIGNMENT.Time, Detail.RESOLVED_TIME) as BusDay
I can use this function for any span that has date data in this (or any query on the database). I will probably be removing the default dates as well as adding a third parameter to count non-business days (UPMC_BUSINESS_DAY = 'f'). But as it is the problem is solved.

Update column when datediff is greater then other column in the same table

I'm struggling with my school assignment, where I have to update a table column based on the difference between two dates. If the difference is lower or equal than column 'maturity' in the table, I had to set column 'Payment' to 'PaymentOK', if the difference is greater than 'maturity', the 'Payment' has to be set to 'Bilker'.
I've tried
#IFNOTFieldExists(TableName=Invoices;FieldName=Payment;)
Alter table Invoices ADD COLUMN Payment TEXT
GO
UPDATE Invoices SET Payment = CASE
WHEN DATEDIFF (Day, Datum, ZaplateneDna) <= Maturity
THEN 'PaymentOK'
ELSE 'Bilker'
END
#ENDIF
Been trying IF THEN statements, UPDATE WHEN CASE statements but I'm still getting syntax errors.
I'm a bit new in sql and I'm unable to combine UPDATE column1 IF DATEDIFF is <= or > then SET Column2 to (based on the <> sign).
Any ideas or advices please?
Thank you in advance, much appreciated.
UPDATE Invoices
SET Payment = CASE
WHEN DATEDIFF(DAY, [Date], DateOfPayment) <= Maturity
THEN 'PaymentOK'
ELSE 'Bilker'
END

SQL Server Update with Case involving another table

I have 2 tables. One has order elements (OE) and one has project information(PO). There are many order elements to 1 project. The way the tables are set up, the project date is is in the PO and the Currency is in the OE. I need to update the Euro Exchange rate in the OE table. I am trying to do something like this
UPDATE [OETest]
SET [Euro Exchange Rate] = {
CASE
WHEN (DATEPART(month, PO.[Project Date Time]) = January)
THEN 8.143296
WHEN (DATEPART(month, PO.[Project Date Time]) = February)
THEN 8.340111
}
FROM [POTest] PO, [OETest] OE
WHERE OE.[Currency] = 'YUAN'
But I am lost (This is one of many queries I have tried). Can anyone help me construct the necessary query and talk me through why it works?
This particular query is telling me there is Incorrect syntax near keyword CASE
To make it more clear what I am trying to accomplish: I have a column for the euro exchange rate in the OE table. I have the average monthly exchange rate that I obtained from a website (not in the table). I want to set this exchange rate column based on the month of the project and the currency. I am going to handle each currency in separate queries so the Yuan is the only currency I'm worried about for this query. The month is in the PO table. I need to use the month from the PO table in the case statement.
UPDATE OE -- the alias here rather than the base table name
SET [Euro Exchange Rate] = 8.143296
FROM [POTest] PO
JOIN [OETest] OE ON OE.project_id = PO.project_id -- you need a link
WHERE OE.[Currency] = 'YUAN'
-- the following date range represents January this year
AND PO.[Project Date Time] >= '20120101'
AND PO.[Project Date Time] < '20120201'
You only need a case statement if you need different values depending on different dates
UPDATE OE -- the alias here rather than the base table name
SET [Euro Exchange Rate] =
CASE Month(PO.[Project Date Time])
when 1 then 8.143296
when 2 then 7.143296
when 3 then 7.743296
END
FROM [POTest] PO
JOIN [OETest] OE ON OE.project_id = PO.project_id -- you need a link
WHERE OE.[Currency] = 'YUAN'
-- the following date range represents 3 months this year
AND PO.[Project Date Time] >= '20120101'
AND PO.[Project Date Time] < '20120401'
What exactly seems to be the problem?
I can see many problems with the query. What are the sql errors you are getting?
IN any case, if I was to try to do the same as what i 'think' you are doing, I would do this:
UPDATE [OETest] OE
SET [Euro Exchange Rate] =
(SELECT CASE WHEN (DATEPART(month, PO.[Project Date Time]) = 'January')
THEN 8.143296
ELSE 0.000000
END ExchageRate
FROM [POTest] PO)
WHERE OE.[Currency] = 'YUAN'
What I am trying to do here is to select the result I want after the SET block. In the CASE statement, I have added an else in the event that I dont get a result from the date part.
The WHERE part is should be a WHERE condition on the UPDATE clause as you need to tell it what records you want to update, unless you want to update all records in that OETest table.
So in summary you are saying
Update the Euro Exchange Rate field with the value from the POTest Table when the month part of the Project Date Time field is equal to January, and update records in the OETest table where the Currency value is 'YUAN'.
Note, that you might need to have a WHERE clause in the SELECT statement as it might return multiple records and you only want one is order to get the Project Date Time field. At a guess, you might want to add a where statement such as follows:
UPDATE [OETest] OE
SET [Euro Exchange Rate] =
(SELECT CASE WHEN (DATEPART(month, PO.[Project Date Time]) = 'January')
THEN 8.143296
ELSE 0.000000
END ExchageRate
FROM [POTest] PO WHERE PO.ProjectID = 100)
WHERE OE.[Currency] = 'YUAN'
Let me know if this helps.
Regards
JT