SQL Server Datetime manipulation - sql

I have the following query below that computes a certain amount based on the Date Differences. However, I could not execute the query because of the error:
Msg 8114, Level 16, State 5, Line 7
Error converting data type varchar to numeric.
Query:
declare #var1 datetime
declare #var2 datetime
set #var1 = '2015-07-14 13:31:43.797'
set #var2 = '2015-07-14 13:31:43.797'
select
'Reefer' =
case
when DATEDIFF (hh,#var1,#var2) <= 6 and DATEDIFF (hh, #var1, #var2) > 0
then 429.000
else
case
when (DATEDIFF (hh,#var1,#var2) % 6) > 0
then 429.00 * ((DATEDIFF (hh,#var1,#var2) / 6)+ 1)
else 'wut'
end
end
from
container con
inner join
containerdetail cod on con.containernumber = cod.containernumber
left join
dea on dea.containernumber = con.containernumber
where
con.billofladingnumber = 'IMPJCP07140003'

You can't use multi types in CASE branches:
Edit else 'wut' to else 0

You have problem because you have different datatypes on THEN and ELSE parts, It should be the same. You can fix It by converting THEN part of INT datatype to NVARCHAR() in following:
case
when (DATEDIFF (hh,#var1,#var2) % 6) > 0
then CAST(429.00 * ((DATEDIFF (hh,#var1,#var2) / 6)+ 1) AS VARCHAR(20))
else 'wut'
end
Also this part then 429.000 should be converted to NVARCHAR():
THEN CAST(429.000 AS NVARCHAR(20))

Related

Add column name to a variable and use it in later calculation in WHERE clause

I have a problem. I need to determine the name of the column under which the calculations will continue. So I wrote a select:
DECLARE #column VARCHAR(MAX)
DECLARE #ColumnA VARCHAR(MAX)
DECLARE #ColumnB VARCHAR(MAX)
SET #ColumnA = 'RegistrationDate'
SET #ColumnB = 'EntryDate'
SET #column = CASE
WHEN CONVERT(DATE,GETDATE()) NOT IN (
'2021-08-04','2021-08-05','2021-08-06','2021-08-07','2021-08-08','2021-08-09','2021-08-10','2021-09-07','2021-09-08','2021-09-09','2021-09-10','2021-09-11',
'2021-09-12','2021-09-13','2021-10-05','2021-10-06','2021-10-07','2021-10-08','2021-10-09','2021-10-10','2021-10-11','2021-11-09','2021-11-10','2021-11-11','2021-11-12','2021-11-13','2021-11-14','2021-11-15','2021-12-07',
'2021-12-08','2021-12-09','2021-12-10','2021-12-11','2021-12-12','2021-12-13'
) THEN
QUOTENAME(#Column)
ELSE
QUOTENAME(#ColumnB)
END
SELECT #column
which returns me [RegistrationDate] or [EntryDate] and stores this in variable #column. Now, when I know under which column should I calculate, I want to insert this variable #column in to my main select one of the WHERE clause:
DECLARE #column VARCHAR(MAX)
DECLARE #ColumnA VARCHAR(MAX)
DECLARE #ColumnB VARCHAR(MAX)
SET #ColumnA = 'RegistrationDate'
SET #ColumnB = 'EntryDate'
SET #column = CASE
WHEN CONVERT(DATE,GETDATE()) NOT IN (
'2021-08-04','2021-08-05','2021-08-06','2021-08-07','2021-08-08','2021-08-09','2021-08-10','2021-09-07','2021-09-08','2021-09-09','2021-09-10','2021-09-11',
'2021-09-12','2021-09-13','2021-10-05','2021-10-06','2021-10-07','2021-10-08','2021-10-09','2021-10-10','2021-10-11','2021-11-09','2021-11-10','2021-11-11','2021-11-12','2021-11-13','2021-11-14','2021-11-15','2021-12-07',
'2021-12-08','2021-12-09','2021-12-10','2021-12-11','2021-12-12','2021-12-13'
) THEN
QUOTENAME(#Column)
ELSE
QUOTENAME(#ColumnB)
END
SELECT
CASE WHEN final.Branch IS NULL THEN 'Total'
ELSE final.Branch
END AS 'Branch',
final.TR
FROM
(
SELECT
CASE
WHEN main.BRANCHNO = 1 THEN 'One'
WHEN main.BRANCHNO = 2 THEN 'Two'
WHEN main.BRANCHNO = 3 THEN 'Three'
WHEN main.BRANCHNO = 4 THEN 'Four'
WHEN main.BRANCHNO = 5 THEN 'Five'
WHEN main.BRANCHNO = 6 THEN 'Six'
END AS 'Branch',
COUNT(*) AS 'TR'
FROM
(
SELECT
*
FROM
[TABLE]
WHERE
Status = 100
AND
BRANCHNO IN (1,2,3,4,5,6)
AND
Type = 'TR'
AND
**#column** = CONVERT(DATE, CASE WHEN DATENAME(dw, getdate()) = 'Monday' THEN getdate()-3 ELSE getdate()-1 END
)
) AS main
GROUP BY
main.BRANCHNO WITH ROLLUP
) AS final
But when I execute query it returns me an error:
Msg 241, Level 16, State 1, Line 11 Conversion failed when converting
date and/or time from character string.
I imagined everything very simple: I put a column name into a variable, and then, that name placed at the beginning of the WHERE clause will be recognized as the column name and then *= CONVERT(DATE, CASE WHEN DATENAME(dw, getdate()) etc will do all work.
But that did not happen. Maybe someone knows why and maybe they know how to solve this task?
You can't use a variable to reference a column name. #column is just a piece of data, which just so happens to contain a column name as a string, but it's still just a string, not actually a reference to a column in a table.
Some options you have seem to be...
AND CASE #column WHEN 'RegistrationDate' THEN RegistrationDate
WHEN 'EntryDate' THEN EntryDate
END
=
CONVERT(DATE, CASE WHEN DATENAME(dw, getdate()) = 'Monday' THEN getdate()-3 ELSE getdate()-1 END)
Or, have two queries which only differ in the column being referenced...
IF (#column = 'RegistrationDate')
<query1>
ELSE IF (#column = 'EntryDate')
<query2>
Or "Dynamic SQL" where you build up a new string with your SQL code and execute that by call sp_executesql (assuming this is SQL Server, which it appears to be).
I recommend reading this : https://www.sommarskog.se/dyn-search.html
EDIT: A pure SQL alternative, assuming SQL Server
DECLARE #mode INT = CASE
WHEN CONVERT(DATE,GETDATE()) NOT IN (
'2021-08-04','2021-08-05','2021-08-06','2021-08-07','2021-08-08','2021-08-09','2021-08-10','2021-09-07','2021-09-08','2021-09-09','2021-09-10','2021-09-11',
'2021-09-12','2021-09-13','2021-10-05','2021-10-06','2021-10-07','2021-10-08','2021-10-09','2021-10-10','2021-10-11','2021-11-09','2021-11-10','2021-11-11','2021-11-12','2021-11-13','2021-11-14','2021-11-15','2021-12-07',
'2021-12-08','2021-12-09','2021-12-10','2021-12-11','2021-12-12','2021-12-13'
) THEN
0
ELSE
1
END;
DECLARE #filter_date DATE = CONVERT(DATE, CASE WHEN DATENAME(dw, getdate()) = 'Monday' THEN getdate()-3 ELSE getdate()-1 END;
WITH
source AS
(
SELECT
*
FROM
[TABLE]
WHERE
Status = 100
AND BRANCHNO IN (1,2,3,4,5,6)
AND Type = 'TR'
),
filtered_source AS
(
SELECT 0 AS mode, * FROM source WHERE RegistrationDate = #filter_date
UNION ALL
SELECT 1 AS mode, * FROM source WHERE EntryDate = #filter_date
)
SELECT
COALESCE(
CASE
WHEN BRANCHNO = 1 THEN 'One'
WHEN BRANCHNO = 2 THEN 'Two'
WHEN BRANCHNO = 3 THEN 'Three'
WHEN BRANCHNO = 4 THEN 'Four'
WHEN BRANCHNO = 5 THEN 'Five'
WHEN BRANCHNO = 6 THEN 'Six'
END,
'Total'
)
AS 'Branch',
COUNT(*) AS 'TR'
FROM
filtered_source
WHERE
mode = #mode
GROUP BY
GROUPING SETS (
(mode),
(mode, BRANCHNO)
);
By always including mode in the GROUPING SETS, the optimiser might be able to yield a better execution plan for the two scenarios.
Still read the link given above though, at the very least to understand why this is necessary, or perhaps why it doesn't quite manage to yield the best execution plan.

TODATETIMEOFFSET inside a case statement

I am trying to make the TODATETIMEOFFSET work inside a case statement. when I try to do this sql returns me following error. works fine if it's not inside a case statement. what am I doing wrong?
SELECT AP.POR,
AP.POD Path,
TODATETIMEOFFSET(AP.StartTime, '-06:00') as StartTime,
MinimumPrice = (
CASE WHEN ( CHARINDEX('','' + '''+#Provider+''' + '','', '','' + '''+#UserCompanyList+''' + '','') > 0 )
THEN TODATETIMEOFFSET(AP.MinimumPrice, '-06:00')
ELSE ( (
CASE WHEN (getdate() < C.ClearingTime and C.OpenPriceMask = 0)
THEN NULL
WHEN (getdate() > C.ClearingTime and C.ClearedPriceMask = 0)
THEN NULL
ELSE AP.MinimumPrice
END
) )
END
),
AP.ClearingPrice,
AP.PriceUnits
FROM TES_Auction C
INNER JOIN TES_AuctionPrice AP ON AP.AuctionID = C.ID
Msg 206, Level 16, State 2, Line 1
Operand type clash: decimal is incompatible with datetime2
also how can I make 'as' syntax work inside a case statement?
It looks like you have an error on this part of your query:
THEN TODATETIMEOFFSET(AP.MinimumPrice, '-06:00')
AP.MinimumPrice I would expect a decimal type, and SQL can't convert a decimal to datetime2.
Also, to use a column alias on a case statement, you would put it at the very end just before the next comma, for example:
ELSE AP.MinimumPrice END))END) AS myColumnName, -- etc.

Multiply value, round down and CAST Select in SQL

I have a SQL function that gathers labour times from multiple records, and uses STUFF(CAST AS VARCHAR(20)) to concatenate them into a single column in a temp table.
My issue is the labour values in my table are in hours and rounded down to 7 decimals, I want to convert those values to minutes and round them down to 2 decimals before stuffing them into my temp table.
Update: Forgot to mention, I'm using SQL Server 2008 R2
Here is my code.
#site nvarchar(20)
,#item nvarchar(30)
,#enviro nvarchar(30))
RETURNS nvarchar(MAX)
AS
BEGIN
DECLARE #LbrHours AS nvarchar(MAX) = ''
IF #enviro = 'Test'
BEGIN
IF #site = 'Arborg'
BEGIN
SET #LbrHours = STUFF((SELECT ',' + CAST(jrt.run_lbr_hrs AS VARCHAR(20))
FROM Arborg_Test_App.dbo.jobroute AS jbr
INNER JOIN Arborg_Test_App.dbo.job AS job
ON job.job = jbr.job
AND job.suffix = jbr.suffix
INNER JOIN Arborg_Test_App.dbo.item AS itm
ON itm.job = job.job
INNER JOIN Arborg_Test_App.dbo.jrt_sch AS jsh
ON jbr.job = jsh.job
AND jbr.suffix = jsh.suffix
AND jbr.oper_num = jsh.oper_num
LEFT OUTER JOIN Arborg_Test_App.dbo.jrt_sch AS jrt
ON jbr.job = jrt.job
AND jbr.suffix = jrt.suffix
AND jbr.oper_num = jrt.oper_num
WHERE job.suffix = '0' and job.type = 'S' AND itm.item IS NOT NULL
AND itm.item = #item
AND jbr.suffix = CASE -- Return Standard cost if Standard Operation exist, else return current cost
WHEN itm.cost_type = 'S'
THEN '1' -- '1' for standard operation
ELSE '0' -- '0' for current operations
END
ORDER BY itm.item, jbr.oper_num
FOR XML PATH('')), 1, 1, '')
END
END
RETURN #LbrHours
END
jrt.run_lbr_hrs is the column that contains the labour times in hours in our ERP's table. How can I multiply that by 60 and round it down to 2 decimals with in my existing STUFF(CASE AS NVARCHAR)?
CAST to decimal with 2 value after the point and then to varchar
CAST(CAST(jrt.run_lbr_hrs * 60 AS DECIMAL(10, 2)) AS VARCHAR(20))
Change the decimal dimension to what you need
Try this Str (jrt.run_lbr_hrs * 60, 2)

Count on SQL Function return value

I am trying to count the number of records in my table that make a function I created return 1. Here is what I have:
CREATE FUNCTION isErrorMismatch
(#theType nvarchar(1), #theExCode nvarchar(2))
RETURNS bit
AS
BEGIN
DECLARE #theTypeAsInt int
SET #theTypeAsInt = CAST(#theExCode AS INT)
DECLARE #returnValue bit
SET #returnValue = 0
IF #theType = 'A'
IF #theTypeAsInt >= 10 AND #theTypeAsInt <= 17
SET #returnValue = 0
ELSE
SET #returnValue = 1
ELSE IF #theType = 'B'
IF #theTypeAsInt >= 18 AND #theTypeAsInt <= 26
SET #returnValue = 0
ELSE
SET #returnValue = 1
ELSE IF #theType = 'C'
IF #theTypeAsInt >= 30 AND #theTypeAsInt <= 38
SET #returnValue = 0
ELSE
SET #returnValue = 1
ELSE
SET #returnValue = 1
RETURN #returnValue
END
GO
SELECT (SELECT COUNT(*)
FROM isErrorMismatch(LEFT(Type, 1),LEFT([Exception Code/Category],2))
As MismatchCount
FROM dbo.[All Service Ticket Data 2012_final]
Every record that makes the function return 1, I want to count. I am getting syntax errors in my FROM when I call the function. Anyone have any ideas? Thank you!
***UPDATE:
In order to get the count that make the function return 1:
SELECT COUNT(dbo.isErrorMismatch(LEFT(Type, 1), LEFT([Exception Code/Category],2))) As MismatchCount
FROM dbo.[All Service Ticket Data 2012_final]
WHERE dbo.isErrorMismatch(LEFT(Type, 1), LEFT([Exception Code/Category],2)) = 1
In order to get all of the records that make the function return 1:
SELECT Type, [Exception Code/Category],
dbo.isErrorMismatch(LEFT(Type, 1),LEFT([Exception Code/Category] ,2)) as Mismatch
FROM dbo.[All Service Ticket Data 2012_final]
WHERE dbo.isErrorMismatch(LEFT(Type, 1),LEFT([Exception Code/Category] ,2)) = 1
Scalar UDF, which in this case accepted two parameters and returned a single value.
Some of the areas where you can use a scalar UDF:
A column expression in a SELECT or GROUP BY
A search condition for a JOIN in a FROM clause
A search condition of a WHERE or HAVING clause
SELECT SUM(CAST(dbo.isErrorMismatch(LEFT(Type, 1), LEFT([Exception Code/Category],2)) AS int)) As MismatchCount
FROM dbo.[All Service Ticket Data 2012_final]
You are having an error because your function is not a table type or does not return a table and so you can NOT select from it. But nevertheless you can achieve it by doing this:
SELECT COUNT(*)
FROM dbo.[All Service Ticket Data 2012_final] a
INNER JOIN
(
SELECT isErrorMismatch(LEFT(Type, 1),LEFT([Exception Code/Category],2)) IsMatched, Your_PK_Column_or_Id
FROM dbo.[All Service Ticket Data 2012_final]
) x ON x.Your_PK_Column_or_Id = a.Your_PK_Column_or_Id
WHERE x.IsMatched = 1
I just want to add that if the value you passed a value to #theExCode that cannot be cast to an INT then there will be an exception in your query.
You need to end the CREATE FUNCTION with GO. Also, your SELECT subquery needs a closing parenthesis at the end.
(Your question was about the syntax errors).

Getting average from 3 columns in SQL Server

I have a table with 3 columns (smallint) in SQL Server 2005.
Table Ratings
ratin1 smallint,
ratin2 smallint
ratin3 smallint
These columns can have values from 0 to 5.
How can I select the average value of these fields, but only compare fields where the value is greater then 0.
So if the column values are 1, 3 ,5 - the average has to be 3.
if the values are 0, 3, 5 - The average has to be 4.
This is kind of quick and dirty, but it will work...
SELECT (ratin1 + ratin2 + ratin3) /
((CASE WHEN ratin1 = 0 THEN 0 ELSE 1 END) +
(CASE WHEN ratin2 = 0 THEN 0 ELSE 1 END) +
(CASE WHEN ratin3 = 0 THEN 0 ELSE 1 END) +
(CASE WHEN ratin1 = 0 AND ratin2 = 0 AND ratin3 = 0 THEN 1 ELSE 0 END) AS Average
#mwigdahl - this breaks if any of the values are NULL. Use the NVL (value, default) to avoid this:
Sum columns with null values in oracle
Edit: This only works in Oracle. In TSQL, try encapsulating each field with an ISNULL() statement.
There should be an aggregate average function for sql server.
http://msdn.microsoft.com/en-us/library/ms177677.aspx
This is trickier than it looks, but you can do this:
SELECT dbo.MyAvg(ratin1, ratin2, ratin3) from TableRatings
If you create this function first:
CREATE FUNCTION [dbo].[MyAvg]
(
#a int,
#b int,
#c int
)
RETURNS int
AS
BEGIN
DECLARE #result int
DECLARE #divisor int
SELECT #divisor = 3
IF #a = 0 BEGIN SELECT #divisor = #divisor - 1 END
IF #b = 0 BEGIN SELECT #divisor = #divisor - 1 END
IF #c = 0 BEGIN SELECT #divisor = #divisor - 1 END
IF #divisor = 0
SELECT #result = 0
ELSE
SELECT #result = (#a + #b + #c) / #divisor
RETURN #Result
END
select
(
select avg(v)
from (values (Ratin1), (Ratin2), (Ratin3)) as value(v)
) as average
You can use the AVG() function. This will get the average for a column. So, you could nest a SELECT statement with the AVG() methods and then SELECT these values.
Pseudo:
SELECT col1, col2, col3
FROM (
SELECT AVG(col1) AS col1, AVG(col2) AS col2, AVG(col3) AS col3
FROM table
) as tbl
WHERE col1 IN (0, 3, 5)
etc.