Issues with updating a SQL table using a CASE statement - sql

I'm fairly new to SQL and still trying to get my head around a few concepts...
I have created a simple table and i'm trying to update one of the columns in the table using a CASE statement. I understand why the statment won't work but i'm confused as to how to fix it....
This is my code:
CREATE TABLE q5
(
UserID INT,
Month INT,
Score INT
)
INSERT INTO q5(UserID, Month, Score)
VALUES (1,1,10), (1,2,5), (1,1,6), (2,8,6), (3,1,9), (3,4,11), (3,6,9), (4,9,10), (5,1,2);
UPDATE q5
SET
Month = CASE WHEN Month IN (1, 2, 3) THEN 'First Quarter'
WHEN Month IN (4, 5, 6) THEN 'Second Quarter'
WHEN Month IN (7, 8, 9) THEN 'Third Quarter'
ELSE 'Fourth Quarter'
END
I'm getting this error:
Msg 245, Level 16, State 1, Line 1
Conversion failed when converting the varchar value 'First Quarter' to data type int.

Month is defined as an int, yet you are trying to update the table and set Month to a varchar, like 'First Quarter'. You can either change Month to store a varchar, and then modify your insert statement and WHEN Month IN pieces, or add an additional column that can store the quarter.
The query below shows the second option (adding an additional column).
CREATE TABLE q5
(
UserID INT,
Month INT,
Score INT,
Quarter varchar(20)
)
INSERT INTO q5(UserID, Month, Score)
VALUES (1,1,10), (1,2,5), (1,1,6), (2,8,6), (3,1,9), (3,4,11), (3,6,9), (4,9,10), (5,1,2);
UPDATE q5
SET
Quarter = CASE WHEN Month IN (1, 2, 3) THEN 'First Quarter'
WHEN Month IN (4, 5, 6) THEN 'Second Quarter'
WHEN Month IN (7, 8, 9) THEN 'Third Quarter'
ELSE 'Fourth Quarter'
END
Depending on how this is being utilized, you might be better off adding a View which would calculate the Quarter. This way, you wouldn't have to maintain that additional column, which is entirely dependant on the Month column.

Your column MONTH can only store data of type INT. What you've got now is similar to the following procedural code:
int month = 2;
if (month == 1 || month == 2 || month == 3)
{
month = "First Quarter"; // compiler error
}
As you can see, that conditionally executed line is fairly nonsensical, since month is of type int.
You're probably looking to add another column, say Quarter.
ALTER TABLE q5 ADD Quarter NVARCHAR(100)
UPDATE q5
SET
Quarter = CASE WHEN Month IN (1, 2, 3) THEN 'First Quarter'
WHEN Month IN (4, 5, 6) THEN 'Second Quarter'
WHEN Month IN (7, 8, 9) THEN 'Third Quarter'
ELSE 'Fourth Quarter'
END
Although realistically, listing the value names should be done by the UI. Best practice would be to use a computed column or separate VIEW that has a Quarter INT column. This code would add a computed column with the appropriate datatype.
ALTER TABLE q5 ADD
Quarter AS CASE WHEN Month IN (1, 2, 3) THEN 1
WHEN Month IN (4, 5, 6) THEN 2
WHEN Month IN (7, 8, 9) THEN 3
ELSE 4
END

Related

SQL converting number of days to weeks

I have a SQL table with a column that says number of days, and contains entries like 23, 26, 45, etc...
I am trying to convert each entry to a "week number". In essence, what I mean is that if my day entry is between 0 and 6, then, this is Week 1, if it is 7 and 13, then this is Week 2, 14 and 20, week 3, etc... Is there an "efficient" way to do this in SQL?
Thanks.
Thomas.
You need just the standard divide function. It ignores the remainder:
SELECT (Days / 7) + 1
You can try this and no need to add +1;
SELECT (Days / 7.00)

Rolling 6 Months

I have 2 parameters in my report, Month and Year.
I also have a table which in one column has the count of rows for that particular month and another column which has the count of rows for that specific month and the next 5 months (Rolling 6 month).
Both of these columns have an expression which links to the parameters as shown below:
Month Expression:
6 Month Expression:
My parameters use specific values which i have put in (1 - 12) so when my rolling month adds 1 value each time as you can probably guess this wont work when I come near the latter months of the year as my expression does not roll over to next year and just stops at December.
Overview: Parameters are integer values 1 - 12 and I am not sure how to calculate a rolling 6 months which will carry over to the next year when selecting months late in the year. e.g. If I select November as my Month parameter, my rolling 6 months will only display the sum of rows for November and December, not going to the next year. I am assuming this is because my month values are integers and in my expression I add numbers to the integers for each month therefore my rolling 6 will be trying to add months 11, 12, 13, 14, 15, 16 when obviously months only go to 12.
This is not an answer to your question, but looking at this, you may be able to find a solution with SQL itself.
See the query below. Consider this as your report source sql.
DECLARE #A TABLE (ID INT IDENTITY(1,1),DT DATE)
INSERT INTO #A (DT)
VALUES
('2013-01-26'),('2013-02-23'),('2013-03-20'),
('2013-04-23'),('2013-05-23'),('2013-07-23'),
('2013-08-23'),('2013-08-29'),('2013-09-23'),
('2013-12-10'),('2014-03-01')
If you join the result with itself, some what like below, you will get the result in sql query itself, you need to only show it in report. See below.
SELECT DATEPART(YEAR,DT) [Y],DATEPART(MONTH,DT) [M],COUNT(*) [ROLLING 6]
FROM (
SELECT A.*
FROM #A A,#A B
WHERE ((DATEPART(YEAR,B.DT) * 12) + DATEPART(MONTH,B.DT)) BETWEEN
(DATEPART(YEAR,A.DT) * 12) + DATEPART(MONTH,A.DT) AND
((DATEPART(YEAR,A.DT) * 12) + DATEPART(MONTH,A.DT) + 6)) LU
GROUP BY DATEPART(YEAR,DT),DATEPART(MONTH,DT)
ORDER BY [Y],[M]

Convert Varchar column to datetime

I have two columns as below. ColumnA is a varchar. 9bd is an assigned number, 10 is fiscal year, 07 is month, and the rest is unique id. ColumnB is datetime.
ColumnA ColumnB
9db1007000001 8/7/2011
I would like to seperate from ColumnA into a Month column (datetime) and a Year column (datetime).
Desired result from ColumnA:
Column Month Column Year
7 2010
OR
7 10
I can seperate ColumnB into Month column and Year column. Here is my attempted SQL script.
SELECT
ColumnA,
DATEPART(mm, (ColumnB)) as Month,
DATEPART(yy, (ColumnB)) as YEAR
FROM MY TABLE
Can someone please help? I did search within this website and found similar question but my attempted script produces error
--Conversion failed when converting datetime from character string.
SELECT
CAST(SUBSTRING(ColumnB, 5, 2) + SUBSTRING(ColumnB, 3, 2) AS DATETIME) Month_Year
FROM MY TABLE
BTW, this DATEPART(yy, (ColumnB)) function returns 2011. How can it return 11 instead?
Or should I submit another question?
the conversion from a string to datetime requires the string in a proper format, which depends on your DB settings too.
for the second question about getting the last two digits, I suggest to use modulo instead of subtraction - you never know whether it's 2000 or 1900, unless you are 100% it's 2000
a quick example:
SELECT SUBSTRING(columnA, 4, 2) AS FiscalMonth,
SUBSTRING(columnA, 6, 2) AS FiscalYear,
CAST(SUBSTRING(columnA, 6, 2) + '-01-' + SUBSTRING(columnA, 4, 2) AS DATETIME) Fiscal, -- in format of YY-dd-MM, but this really depends on your settings
DATEPART(MM, ColumnB) AS [Month],
DATEPART(YY, ColumnB) % 100 AS [Year]
FROM yourTable;

Subtract 4 months from a pair of year/month values

I want to subtract 4 months, the period is defined as year and month:
UPDATE [MAS_YCA].[dbo].[temp_AR_SalesPersonhistory]
SET FiscalYear = year(DATEADD(month,-4,DATEADD(DAY,-1,DATEADD(month,cast(FiscalPeriod as Int),DATEADD(year,cast(FiscalYear as Int)-1900,0))))),
FiscalPeriod = right('00'+cast(month(DATEADD(month,-4,DATEADD(DAY,-1,DATEADD(month,cast(FiscalPeriod as Int),DATEADD(year,cast(FiscalYear as Int)-1900,0))))) as varchar),02)
GO
The error I'm getting is Adding value to a datetime column caused an overflow.
The fields fiscal year and period are both defined as varchar in the datable.
Sounds like you need to check the data in the FiscalYear and FiscalPeriod columns. Most likely, you have an invalid year in the Fiscal year column.
The date range in SQL Server is January 1, 1753, through December 31, 9999. So any year outside this will cause your error.
An easy check (edit, added null and empty string cases):
Select * from [MAS_YCA].[dbo].[temp_AR_SalesPersonhistory]
where cast(FiscalYear as Int) > 9999 or cast(FiscalYear as Int) < 1753
or FiscalYear is NULL or FiscalYear = ''

SQL Server 2005 Bitwise Which Day is next?

I have a table called Jobs that keeps track of jobs and their next run time. One particular scheduling options allows a job to run several times per week. I use bitwise comparisons to discern which day comes next (Well... I'm trying to anyway.) So for example. I have a table like this..
JobID NextRunTime DaysOfWeek
1 12-26-2011 21
My bitwise enumeration is like this..
Monday = 1
Tuesday = 2
Wednesday = 4
Thursday = 8
Friday = 16
Saturday = 32
Sunday = 64.
So we know that this job should run on Monday, Wednesday, Friday. (12-26-2011) is a Monday, so when it updates, it should run again on 12-28-2011 but I am unable to come up with an algorithm that allows me to do programmatically set the new NextRunTime.
This is the method I'm currently trying to get to work with some pseudo-code for what I'm having problems with..
IF OBJECT_ID('tempdb..#DaysSchedule') IS NOT NULL DROP TABLE #DaysSchedule
CREATE TABLE #DaysSchedule
(
Monday int, Tuesday Int, Wednesday Int, Thursday INT, Friday INT, Saturday INT, Sunday INT
)
INSERT INTO #DaysSchedule (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)
Values(21 & 1,21 & 2,21 & 4,21 & 8,21 & 16 ,21 & 32,21 & 64)
This gives us a table that looks like this:
Monday Tuesday Wednesday Thursday Friday Saturday Sunday
1 0 4 0 16 0 0
From here the (half) pseudo-code is easy.
for (int i=1; i<7, i++)
{
thisDay = DATENAME(dw, DATEADD(day, i, nextRunTime)) -- we add one day
if (column named thisDay contains a value > 0) -- if that days value > 0
begin
We add the difference of thisDay to NextRunTime to NextRunTime and we're done.
end
}
NOTE: I'm not going to comment on the idea of representing multiple items of data in a single field. It may or may not be appropriate in this case, I'm just commenting on how to make this type of idea work.
The problem that you are facing is that the information does not actually closely match its use.
At present...
- Extract the DAY from NextRunTime
- Identify the BIT representing that day
- SEARCH for the next bit set to one, cycling around to the start if necessary
- Identify the distance traveled in that search
- Add that distance to NextRunTime
It's just not efficient or simple.
I would recommend instead recording the number of days to add to reach the next planned date.
Examples:
-----15 = Saturday and Sunday Only
1111111 = Every Day
11113-- = Every Weekday
2-2-3-- = Monday, Wednesday, Friday
This changes the algorithm to...
- Extract the DAY from NextRunTime
- Identify the character in that position
- Cast it to an INT
- Add that many days to NextRunTime
This avoids a search and count section, replacing it with a straight look-up.
It does allow 'dead-ends', or more complex plans. This may be an advantage or dis-advantage depending on your situation...
1111100 = Every weekday for a week, then stop
2222222 = Every other day, on a two week cycle
Would it be so bad to use three rows to model the three days? e.g.
INSERT INTO Jobs (JobID, NextRunTime, RepeatOption)
VALUES (1, '2011-12-26', 'Y');
INSERT INTO RepeatJobs (JobID, RepeatOption, DaysOffset)
VALUES (1, 'Y', 2),
(1, 'Y', 4);
If you must go with bitwise, how about creating a lookup table e.g.
VALUES (1, 'Monday'),
(2, 'Tuesday'),
(3, 'Monday'),
(3, 'Tuesday'),
(4, 'Wednesday'),
(5, 'Monday'),
(5, 'Wednesday'),
(6, 'Tuesday'),
(6, 'Wednesday'),
(7, 'Monday'),
(7, 'Tuesday'),
(7, 'Wednesday'),
(8, 'Thursday'),
(9, 'Monday'),
(9, 'Thursday'),
(10, 'Tuesday'),
(10, 'Thursday'),
(11, ...
...but rather than 'Monday', 'Tuesday', 'Wednesday' etc store the offset in days from a set day of the week, say Sunday, then round down your NextRunTime to the Sunday then add the offset etc.