I have a "case when" in my computed column, but I can't figure out how to throw an exception.
Here's sample code that doesn't work...
CREATE TABLE OrderDetail
( OrderID INT
, ProductID INT
, Qty INT
, OrderDate DATETIME
, ShipDate DATETIME
, STATUS AS CASE
WHEN shipdate is NULL AND orderdate < DATEADD( dd, -7, GETDATE()) THEN 3
WHEN shipdate is NOT NULL THEN 2
ELSE RAISERROR ('Error in shipdate',-1,-1)
end
)
GO
But it is invalid.
Isn't it possible to raise an error in computed columns?
This can't be done like this. A case expression can't be used as a flow control. It's specifically documented:
The CASE expression cannot be used to control the flow of execution of Transact-SQL statements, statement blocks, user-defined functions, and stored procedures.
You can add a check constraint to the table, but that would not allow you to raise your own custom error:
CREATE TABLE OrderDetail
(
OrderID INT
, ProductID INT
, Qty INT
, OrderDate DATETIME
, ShipDate DATETIME
, STATUS AS CASE
WHEN shipdate is NULL AND orderdate < DATEADD( dd, -7, GETDATE()) THEN 3
WHEN shipdate is NOT NULL THEN 2
ELSE NULL
END
, CONSTRAINT Chk_OrderDetails_Dates CHECK(
shipdate IS NOT NULL
OR orderdate < DATEADD( dd, -7, GETDATE())
)
)
GO
Or you can use triggers - instead of insert and instead of update to only allow rows where the dates are valid.
Personally, I would go with a check constraint - It's safe and more easier to write and maintain.
Related
Working with Parameters and SQL in a SSRS report
#FromDate and #ToDate can be null -- To get all 4 records
#Fromdate can have a date value while #ToDate is null - if this is the case need to get all date value which does not have TravelTo Date
#Fromdate and #todate has values then need to get the last 2 records
#FromDate and #ToDate is null then need to get all value
I used the below code to get the data but i am not getting the right data ,any suggestion is appreciated.
Where
(TravelTo BETWEEN #FROMSTART AND #TODATE)
or(#TODATE IS NULL and TravelFrom >=#FROMSTART )
OR(#FROMSTART IS NULL AND TravelFrom <=#TODATE)
OR(#FROMSTART IS NULL AND #TODATE IS NULL)
Parmater screenshot from SSRS
Assuming the either the date inputs or the columns themselves could be NULL, you may try:
SELECT *
FROM yourTable
WHERE
(#FROMSTART > TravelFrom OR #FROMSTART IS NULL OR TravelFrom IS NULL) AND
(#TODATE < TravelTo OR #TODATE IS NULL OR TravelTo IS NULL);
This logic would discount either the start or end of the date range should either the column have a NULL value, or the input have a NULL value.
SELECT *
FROM TABLE1
WHERE ((#FROMSTART IS NULL AND #TODATE IS NULL)
OR(#FROMSTART IS NULL AND TravelTo <=#TODATE)
OR(#TODATE IS NULL AND TravelFrom >=#FROMSTART)
OR(TravelFrom >= #FROMSTART AND TravelTo<=#TODATE))
Hope this Query Works fine for your case:
You had forgot to mentioned these conditions within an Open/Close bracket.So that only you cannot get the right output.
I think the problem is the 2nd condition
#Fromdate can have a date value while #ToDate is null - if this is the case need to get all date value which does not have TravelTo Date
Please try the code below which does (I think) what you specified in your question. If this does not work, please supply a set of test dates and expected outcome for each test as some are a little ambiguous.
NOTE: I have used standard date format for the dates as these will not suffer from conversion to and from the MM/DD/YYYY format you are using.
DECLARE #t TABLE (ID int, TravelFrom date, TravelTo date)
INSERT INTO #t VALUES
(9759497, '2020-02-01', NULL),
(5226416, '2020-01-15', NULL),
(2731975, '2020-03-01', '2020-04-30'),
(2318260, '2020-01-01', '2020-02-28')
DECLARE #fromDate date = '2020-02-01'
DECLARE #toDate date = NULL
SELECT * FROM #t
WHERE
(TravelFrom >= #fromDate or #fromDate is null)
AND
(TravelTo <= #toDate or #toDate is null)
AND
(
CASE WHEN #toDate IS NULL THEN 1 ELSE 0 END
=
CASE WHEN TravelTo IS NULL THEN 1 ELSE 0 END
)
I'm trying to update a date in a temporary table using a parameter and looking at the last row number's date.
DECLARE #multiDayCourseDaysBetween INT = 3;
CREATE TABLE #Courses(TempId INT IDENTITY(1,1)
, [Date] DATE
, CourseTypeId INT
, OrganisationId INT
, Reference VARCHAR(100)
, CreatedByUserId INT
, CourseTypeCategoryId INT
, TrainersRequired INT);
CREATE TABLE #TempDates(TempId INT
, [Date] DATE
, LagDate DATE);
INSERT INTO #Courses([Date])
Values('2016-06-01')
INSERT INTO #Courses([Date])
Values('2016-06-02')
INSERT INTO #Courses([Date])
Values('2016-06-03')
INSERT INTO #TempDates(tempId, [date], LagDate)
SELECT TempId, [Date]
, LAG(c.[Date],1) OVER (ORDER BY [Date]) as LagDate
FROM #Courses c
UPDATE #TempDates
SET [Date] = DATEADD(dd, #multiDayCourseDaysBetween, LAG([Date],1) OVER (ORDER BY [Date]))
WHERE LagDate IS NOT NULL
But I receive an error - 'Windowed functions can only appear in the SELECT or ORDER BY clauses.'
For example the original dates would be
2016-06-01
2016-06-02
2016-06-03
but I would need them to become
2016-06-01
2016-06-04
2016-06-07
based off of 3 as a parameter.
Thanks for any help
Try changing the last statement to something like below :
WITH b AS (
SELECT
TempId
, [Date]
, FIRST_VALUE([Date]) OVER (ORDER BY [Date]) as FirstDate
, ROW_NUMBER() OVER (ORDER BY [Date]) AS rowRank
FROM
#TempDates
)
UPDATE b
SET [Date] = DATEADD(day, (rowRank-1)* #multiDayCourseDaysBetween, FirstDate)
WHERE
rowRank > 1;
What am I doing wrong here?
I want to write a function that returns a table of all the products that we have had dismal
performance for between two dates - where 'dismal' means marketing costs exceeded profits. I
have three tables: products, spend and transactions.
CREATE TABLE products
(id int not null, name varchar(255), primary key (id));
CREATE TABLE spend
(id int not null, spenddate date, spendamount decimal (9, 2));
CREATE TABLE transactions
(id int not null, transactiondate date, profit decimal (9, 2));
What I'd do is union queries between the two tables and then sum them to get a line per
product:
WITH a as (
SELECT products.id, products.name,
sum(transactions.profit) as profit,
sum(0) as spendamount
FROM transactions
WHERE transactiondate BETWEEN startdate AND enddate
GROUP BY products.id, products.name
UNION ALL
SELECT id, sum(0) as profit
sum(amount) as spendamount
FROM spend
WHERE spenddate BETWEEN startdate AND enddate
GROUP BY id)
SELECT products.id, products.name, sum(profit)-sum(spendamount) as loss
FROM a, products
WHERE products.id = a.id
GROUP BY products.id, products.name
HAVING sum(profit)-sum(spendamount) < 0;
But I don't want to keep changing the start and enddate values every time I run my code.
I thought I'd do this:
CREATE FUNCTION report_dismal_products (startdate date, enddate date,
out id int,
out name varchar(255),
out loss decimal(9, 2)
)
RETURNS SETOF RECORD
as $$
SELECT products.id, products.name, sum(profit)-sum(spendamount) as loss
FROM
(
SELECT id,
sum(transactions.profit) as profit,
sum(0) as spendamount
FROM transactions
WHERE transactiondate BETWEEN startdate AND enddate
GROUP BY id
UNION ALL
SELECT id, sum(0) as profit
sum(amount) as spendamount
FROM spend
WHERE spenddate BETWEEN startdate AND enddate
GROUP BY id) a, products
WHERE products.id = a.id
GROUP BY products.id, products.name
HAVING sum(profit)-sum(spendamount) < 0;
$$ Language sql;
But it returns
ERROR: column "startdate" does not exist
LINE 17: WHERE transactiondate BETWEEN startdate AND enddate
The DB I am working in stores their date values as INT. I am needing to determine a date difference between two date fields but am unsure how to approach this since they are currently INT.
Dates are stored as follows: 20130101 - YYYYDDMM. Performing a DATEDIFF results in an arithmetic overflow error.
Any ideas on how to either convert two date fields or to find the date difference between them?
Select Cast( Cast( 20130101 As varchar(8) ) As datetime )
So, if you have the following two values: 20130101, 20130201, we might do the following:
Select DateDiff( d
, Cast( Cast( 20130101 As varchar(8) ) As datetime )
, Cast( Cast( 20130201 As varchar(8) ) As datetime ) )
Update
If you have values that less than 17530101 (the min value for a datetime), then you will need to use a Case expression to validate the data as it is processed. In this scenario, the use of Cast(0 As datetime) will result in 1900-01-01 as our minimum date value.
Select DateDiff( d
, Case
When Value1 <= 19000101 Then Cast(0 As datetime)
Else Cast( Cast( Value1 As varchar(8) ) As datetime )
End
, Case
When Value2 <= 19000101 Then Cast(0 As datetime)
Else Cast( Cast( Value2 As varchar(8) ) As datetime )
End )
If you are going to store the dates as integers as shown, a better idea would be to correct the data and add a check constraint that prevents values lower than 1900-01-01. If you have legitimate values lower than 1900-01-01, that will require yet another adjustment to the solution.
I have a stored procedure in an old SQL 2000 database that takes a comment column that is formatted as a varchar and exports it out as a money object. At the time this table structure was setup, it was assumed this would be the only data going into this field. The current procedure functions simply this this:
SELECT CAST(dbo.member_category_assign.coment AS money)
FROM dbo.member_category_assign
WHERE member_id = #intMemberId
AND
dbo.member_category_assign.eff_date <= #dtmEndDate
AND
(
dbo.member_category_assign.term_date >= #dtmBeginDate
OR
dbo.member_category_assign.term_date Is Null
)
However, data is now being inserted into this column that is not parsable to a money object and is causing the procedure to crash. I am unable to remove the "bad" data (since this is a third party product), but need to update the stored procedure to test for a money parsable entry and return that.
How can I update this procedure so that it will only return the value that is parsable as a money object? Do I create a temporary table and iterate through every item, or is there a more clever way to do this? I'm stuck with legacy SQL 2000 (version 6.0) so using any of the newer functions unfortunately is not available.
Checking for IsNumeric may help you - you can simply return a Zero value. If you want to return a 'N/a' or some other string value
I created the sample below with the columns from your query.
The first query just returns all rows.
The second query returns a MONEY value.
The third one returns a String value with N/A in place of the non-integer value.
set nocount on
drop table #MoneyTest
create table #MoneyTest
(
MoneyTestId Int Identity (1, 1),
coment varchar (100),
member_id int,
eff_date datetime,
term_date datetime
)
insert into #MoneyTest (coment, member_id, eff_date, term_date)
values
(104, 1, '1/1/2008', '1/1/2009'),
(200, 1, '1/1/2008', '1/1/2009'),
(322, 1, '1/1/2008', '1/1/2009'),
(120, 1, '1/1/2008', '1/1/2009')
insert into #MoneyTest (coment, member_id, eff_date, term_date)
values ('XX', 1, '1/1/2008', '1/1/2009')
Select *
FROM #MoneyTest
declare #intMemberId int = 1
declare #dtmBeginDate DateTime = '1/1/2008'
declare #dtmEndDate DateTime = '1/1/2009'
SELECT
CASE WHEN ISNUMERIC (Coment)=1 THEN CAST(#MoneyTest.coment AS money) ELSE cast (0 as money) END MoneyValue
FROM #MoneyTest
WHERE member_id = #intMemberId
AND #MoneyTest.eff_date <= #dtmEndDate
AND
(
#MoneyTest.term_date >= #dtmBeginDate
OR
#MoneyTest.term_date Is Null
)
SELECT
CASE WHEN ISNUMERIC (Coment)=1 THEN CAST (CAST(#MoneyTest.coment AS money) AS VARCHAR) ELSE 'N/a' END StringValue
FROM #MoneyTest
WHERE member_id = #intMemberId
AND #MoneyTest.eff_date <= #dtmEndDate
AND
(
#MoneyTest.term_date >= #dtmBeginDate
OR
#MoneyTest.term_date Is Null
)
Apologies for making a new answer, where a comment would suffice, but I lack the required permissions to do so. Onto the answer to your question, I would only like to add that you should use the above ISNUMERIC carefully. While it works much as expected, it also parses things like '1.3E-2' as a value numeric, which strangely enough you cannot cast into a numeric or money without generating an exception. I generally end up using:
SELECT
CASE WHEN ISNUMERIC( some_value ) = 1 AND CHARINDEX( 'E', Upper( some_value ) ) = 0
THEN Cast( some_value as money )
ELSE Cast( 0 as money )
END as money_value