SQL job to ALTER VIEW - tsql

On a weekly base, data is updated from a source into one of the two identical tables: Table_ODD or Table_EVEN. A view transfers data from the active table to a reporting tool. Which table is currently active is stored in a table.
So the view is either like this:
Create View [dbo].[V_ReportData] AS SELECT * FROM [Table_ODD] -- for odd-week
or
Create View [dbo].[V_ReportData] AS SELECT * FROM [Table_EVEN] -- for even-week
The switch may only be performed after approval of the updated data, and will be done by an administrator
who will manually start the job.
I had like to create a job that will do the switch. I have tried this code:`
Declare #NumberOfTables as int
,#ActiveTable as nvarchar(100);
Set #NumberOfTables = (Select Count(*) from [Table_Status] where Upper([Status]) = 'ACTIVE' ) ;
if #NumberOfTables = 1
BEGIN
Set #ActiveTable = (Select UPPER([TABLE_NAME]) from [Table_Status] where Upper([Status]) = 'ACTIVE' );
END;
if #ActiveTable = 'TABLE_EVEN'
BEGIN
ALTER View [dbo].[V_TABLE_] AS
SELECT *
FROM [dbo].[TABLE_ODD]
UPDATE [Table_Status]
Set [STATUS] = 'ACTIVE'
Where [TABLE_NAME] = 'TABLE_ODD'
UPDATE [Table_Status]
Set [STATUS] = 'PASSIVE'
Where [TABLE_NAME] = 'TABLE_EVEN'
END
if #ActiveTable = 'TABLE_ODD'
BEGIN
ALTER View [dbo].[V_TABLE_] AS
SELECT *
FROM [dbo].[TABLE_EVEN]
UPDATE [Table_Status]
Set [STATUS] = 'ACTIVE'
Where [TABLE_NAME] = 'TABLE_EVEN'
UPDATE [Table_Status]
Set [STATUS] = 'PASSIVE'
Where [TABLE_NAME] = 'TABLE_ODD'
END
`
But The ALTER VIEW statement may not be included in any part of the coding.
So I think the ALTER VIEW statement should be in a separate step.
But that step must be activated conditionaly, namely based on the condition which table is active.
In short something like this:
if #ActiveTable = 'TABLE_EVEN'
then step 2
else step 3
DROP and CREATE view is not recommended because of authorizations that are granted to the view. With a DROP VIEW statement, all authorizations are drop as well.
How can I achieve this?

I took me a little while , but found the answer!
I had to create multiple steps first, before I could switch to the specific job step based on an error-code.
List of job steps
step 1: check if switch is valid, e.g. only one table is Active and DateTimeStamp of passive table (which should be the last updated) is more recent then active table
Declare #NumberOfTables as int
,#ActiveTable as nvarchar(100)
,#Update_DateTime_Active as datetime
,#Update_DateTime_Passive as datetime;
Set #NumberOfTables = (Select Count(*) from [Table_Status] where Upper([Status]) = 'ACTIVE' ) ;
if #NumberOfTables = 1
BEGIN
set #Update_DateTime_Active = (Select [UPDATED] from [Table_Status] where Upper([Status]) = 'ACTIVE' );
set #Update_DateTime_Passive = (Select [UPDATED] from [Table_Status] where Upper([Status]) <> 'ACTIVE' );
END
IF #Update_DateTime_Active > #Update_DateTime_Passive
RAISERROR ('Status Active table not compatible with Update DateTimestamp',16,2)
if #NumberOfTables <> 1
RAISERROR ('Tables Data_ODD and Data_EVEN have same status',16,3)
Step 2: determin which table is currently active
Declare #NumberOfTables as int
,#ActiveTable as nvarchar(100);
Set #NumberOfTables = (Select Count(*) from [Table_Status] where Upper([Status]) = 'ACTIVE' ) ;
if #NumberOfTables = 1
BEGIN
Set #ActiveTable = (Select UPPER([TABLE_NAME]) from [Data_Table_Status] where Upper([Status]) = 'ACTIVE' );
END
if #ActiveTable = 'DATA_EVEN'
BEGIN
RAISERROR('Now switch VIEW to table ...ODD',1,1)
END
This step determin the next step to be taken in the advanced settings of the step:
On succes action: goto step 3
on Error action: goto step 5
Remember: when ..EVEN is active, an error is raised, causing the job to go to step 5 (which is activating ..ODD)
Step 3: alter view to EVEN
ALTER View [dbo].[V_Data_] AS SELECT * FROM [dbo].[Data_EVEN]
step 4: updated status table (change status of ODD to Active, and EVEN to Passive)
UPDATE [Data_Table_Status]
Set [STATUS] = 'ACTIVE'
Where [TABLE_NAME] = 'Data_EVEN'
UPDATE [Data_Table_Status]
Set [STATUS] = 'PASSIVE'
Where [TABLE_NAME] = 'Data_ODD'
Step 5: alter view to ODD
step 6: updated status table (change status of ODD to Active, and EVEN to Passive)

Related

TSQL: Multiple IF conditions in a stored procedure

Hope I can get suggestions.
I created a stored procedure, but it is not working as intended. Basically if a value exists in a table then it will just select it, else it will insert rows to that table.
TABLE_A -- This is where I need to check if the code exists
CODE
LETTER
GREEEN
A
YELLOW
B
TABLE_B -- I use this table to get letters, there are only 2 groups.
GROUP
LETTERS
1
A
1
B
2
C
This is the stored procedure I made
CREATE PROCEDURE GET_CODE
DECLARE
#CODE VARCHAR(5) = 'RED'
,#GROUP INT = '1'
AS
BEGIN
DECLARE
#GROUPID dbo.UniqueID
,#COUNT = (SELECT count(Code) from TABLE_A where [Code] = #CODE)
/** Get letters **/
IF #GROUP = '1'
begin
insert into #GROUPID
select LETTERS from TABLE B
where GROUP = #GROUP
end
else IF #GROUP = '2'
begin
insert into #GROUPID
select LETTERS from TABLE B
where GROUP = #GROUP
end
/** if not exists insert, else go to next step **/
IF #COUNT = 0
begin
insert into TABLE_A VALUES
select
#CODE
,LETTERS
FROM #GROUPid
end
/** select where #code **/
SELECT * FROM TABLE_A where CODE = #CODE
END
Works well if the code exist, but if the code does not exist, it does not insert to TABLE_A
You're sproc is never going to insert into TABLE_A because you did not tell it to. You are inserting into the variable #GROUPID.
This will create a temporary table (note the #) and populate it with a row
DROP TABLE IF EXISTS #table_a
CREATE TABLE #table_a (code NVARCHAR(10), groupID INT)
INSERT INTO #table_a (code, groupID) VALUES
('Green', 2)
SELECT *
FROM #table_a
code groupID
---------------
Green 2
If we then do what you're attempting (outside of a stored procedure for now):
DECLARE #code NVARCHAR(10) = 'Red', #groupID INT = 1
IF NOT EXISTS (SELECT 1 FROM #table_a WHERE #code = code AND groupID = #groupID)
BEGIN
INSERT INTO #table_a (code, groupID) VALUES
(#code, #groupID)
END
SELECT *
FROM #table_a
WHERE code = #code
AND groupID = #groupID
code groupID
---------------
Red 1
First we declare some variables, and give them values.
Then we look to see if the values exist in the table using NOT EXISTS.
If that returns true we INSERT those values into #table_a.
Since we now know that they will exist (they either did already, or we just inserted them) we can then just select those values.
If we do the same thing again, but this time with existing values we can see it doesn't insert them again:
SET #code = 'Red'
SET #groupID = 1
IF NOT EXISTS (SELECT 1 FROM #table_a WHERE #code = code AND groupID = #groupID)
BEGIN
INSERT INTO #table_a (code, groupID) VALUES
(#code, #groupID)
END
SELECT *
FROM #table_a
WHERE code = #code
AND groupID = #groupID
code groupID
Red 1

Update SQL rows one row at a time

I am trying to do a mass update on our SQL server which would easily be done by the following code in our situation.
update patient set security_level = '2'
where security_level = '1'
The problem is our SQL server connects to a mirth server that sends transactions to the State and the mirth server locks up if more than one row is updated at a time, so I'm hoping there is a way to update the rows one at a time. To prevent that our vendor who makes the software has several triggers in place. A piece of the code for the trigger is
IF (#numrows > 1)
BEGIN
IF ##TRANCOUNT > 0 ROLLBACK TRANSACTION
SELECT #errmsg = OBJECT_NAME(##PROCID) + ' : more than one row is updated in table Patient'
RAISERROR(#errmsg,16,21)
RETURN
END
If I were to disable the 3 triggers it would break other things. Thank you for any suggestions or thoughts you have.
declare #tmp_table table(
PRG int identity(1,1) Primary Key,
patient_id int
)
declare #start_value int = 1,
#finish_value int,
#patient_id int
Insert Into #tmp_table(patient_id ) Select patient_id From patient where security_level = '1'
Select #finish_value = max(PRG) From #tmp_table
While #start_value <= #finish_value
Begin
--now get a key for patient and store in variables
Select #patient_id = patient_id
From #tmp_table
Where PRG = #start_value
--.. and now update by key
Update patient
set security_level = '2'
Where patient_id = #patient_id
Set #start_value = #start_value + 1
End

Using a CASE Expression in the Default Value

I am creating a script for my dba to run. In development I have added a new column to an existing table. When I run the following script I have separated the ALTER and UPDATE on my table. I was trying to combine the statements into one. Any suggestions? Not sure if this is possible or I am on the right track. Thanks in advance!
Current Script that is working fine:
if not exists(select * from sys.columns where Name = N'NewColumn' and
Object_ID = Object_ID(N'TableA'))
begin
ALTER TABLE dbo.TableA
ADD NewColumn BIT NULL
DEFAULT 0
end
if exists(select * from sys.columns where Name = N'NewColumn' and Object_ID = Object_ID(N'TableA'))
begin
UPDATE dbo.TableA
SET Newcolumn = CASE WHEN CodeID IN ('A','B') THEN 1 END
WHERE CodeID IN ('A','B')
end
Trying to combine the ALTER and UPDATE into one:
if not exists(select * from sys.columns where Name = N'NewColumn' and Object_ID = Object_ID(N'TableA'))
begin
ALTER TABLE dbo.TableA
ADD NewColumn BIT NULL
DEFAULT case CodeID
WHEN 'A' THEN 1
WHEN 'B' THEN 1
end
I get the following error:
The name "CodeID" is not permitted in this context. Valid expressions are constants, constant expressions, and (in some contexts) variables. Column names are not permitted.
I haven't actually tested this, but I noticed you used different syntax for the CASE statement in the working script and the non-working script. Try this:
if not exists(select * from sys.columns where Name = N'NewColumn' and Object_ID = Object_ID(N'TableA'))
begin
ALTER TABLE dbo.TableA
ADD NewColumn BIT NULL
DEFAULT case WHEN CodeID = 'A' THEN 1 ELSE 0 END
end
But it would not surprise me if you can't reference another column in the default for a column. For example, what happens when you insert a new record without specifying a value for NewColumn? At insert time, CodeID doesn't have a value because the row doesn't exist yet.
Ok, so i did some more digging around. The following solves my problem:
if not exists(select * from sys.columns where Name = N'NewColumn' and
Object_ID = Object_ID(N'TableA'))
begin
ALTER TABLE dbo.TableA
ADD NewColumn BIT NULL
DEFAULT 0 with Values
end
if exists(select * from sys.columns where Name = N'NewColumn' and Object_ID = Object_ID(N'TableA'))
begin
UPDATE dbo.TableA
SET Newcolumn = 1 WHERE CodeID IN ('A','B')
end
I was over complicating things. In order to combine the update I would have to try and add a trigger. This works for what I need to do. Thanks.

tsql trigger behaves abnormally

I have a trigger which is as follows:
ALTER TRIGGER [trigger_CATEGORY_VALUE_ID] ON [dbo].[tblA]
FOR UPDATE
AS
SET NOCOUNT ON
IF ( UPDATE([CATEGORY_VALUE_ID]))
BEGIN
INSERT INTO [dbo].[htblB]
( ID
, CATEGORY_VALUE_ID
, STATUS_END_DATE
, STATUS_END_DATE_SOURCE)
SELECT
t.ID
, t.CATEGORY_VALUE_ID
, GETDATE()
, t.UPDATE_SOURCE
FROM [dbo].[tblCAPITATION] t
INNER JOIN inserted ins
ON t.CATEGORY_VALUE_ID = ins.CATEGORY_VALUE_ID
END
What it needs to do is insert a new row in htblB when the column CATEGORY_VALUE_ID is updated. It works fine if only one row is updated. But if it has multiple row updates, then 2 to the power number of rows updated amount of new rows are inserted in htblB.
UPDATE dbo.tblCAPITATION
SET CAPITATION_STATUS_CATEGORY_VALUE_ID = '80574', UPDATE_SOURCE = 'TEST3'
WHERE CAPITATION_ID = 2 OR CAPITATION_ID = 3
This statement will insert 4 new rows to htblB instead of 2.
May you please shed some light on why this is hapening and how to prevent it?
Thanks!
I'm going to assume that ID is the primary key, if so then you should be joining inserted on ID not category_value_id
ALTER TRIGGER [trigger_CATEGORY_VALUE_ID] ON [dbo].[tblA]
FOR UPDATE
AS
SET NOCOUNT ON
IF ( UPDATE([CATEGORY_VALUE_ID]))
BEGIN
INSERT INTO [dbo].[htblB]
( ID
, CATEGORY_VALUE_ID
, STATUS_END_DATE
, STATUS_END_DATE_SOURCE)
SELECT
t.ID
, t.CATEGORY_VALUE_ID
, GETDATE()
, t.UPDATE_SOURCE
FROM [dbo].[tblCAPITATION] t
INNER JOIN inserted ins
ON t.ID = ins.ID
END

TSQL Hack needed for getting a filter for data

A UI (before the report shows) shows a look up (Combo) that has
(ID = 0).All Organization Units
(ID =4).HR
(ID = 5).DEV
I need to:
Be able to show data of (4) + (5) if
(0) is selected.
Only (4) OR (5) if either HR or DEV is selected.
Lookup combo code (Selected Feeds the parameter in the below query.)
Select 0 AS ID,'All Org' AS Name from DP_ORG_OrganizationUnit
where DP_ORG_OrganizationUnit.Code IN {AccessData}
Union
SELECT
DP_ORG_OrganizationUnit.ID,
DP_ORG_OrganizationUnit.Name
FROM DP_ORG_OrganizationUnit where DP_ORG_OrganizationUnit.Code IN ('HR','DEV')
Report data row query
SET CONCAT_NULL_YIELDS_NULL OFF
DECLARE #EmpID as int;
DECLARE #OrganizationUnit as int;
DECLARE #StartDate as datetime;
DECLARE #EndDate as datetime;
SET #EmpID = ?;
SET #StartDate = ?;
SET #EndDate = ?;
SET #OrganizationUnit = ?;
SELECT
Employee.Code,
Employee.Name1+' '+Employee.Name2+' '+Employee.Name3+' '+Employee.Name4+' '+Employee.Name5 AS FullName,
Employee.OrganizationUnit,
ContractType.Name,
EmployeeContract.StartDate,
EmployeeContract.EndDate
FROM Employee INNER JOIN (ContractType INNER JOIN EmployeeContract
ON ContractType.ID = EmployeeContract.ContractType)
ON Employee.ID = EmployeeContract.Employee
WHERE (Employee.ID = #EmpID OR #EmpID=0)
AND
(Employee.OrganizationUnit = #OrganizationUnit OR #OrganizationUnit=0)
AND NOT((EndDate < #StartDate or StartDate > #EndDate));
Any way I can achieve it from the looks of it? 0=0 would show all the data from other
departments too..
Anybody :-o?
First off, your lookup combo code could be tightened up a bit:
-- the FROM clause was superfluous
SELECT 0 AS ID,'All Org' AS Name
UNION ALL
-- the two-part identifiers were superfluous (only one table)
SELECT ID, Name
FROM DP_ORG_OrganizationUnit
WHERE Code IN ('HR','DEV')
For the report query, the simplest form would be:
WHERE
((#OrganizationUnit > 0 AND Employee.OrganizationUnit = #OrganizationUnit) OR
(#OrganizationUnit = 0 AND Employee.OrganizationUnit IN (4,5)))
something like this should work
Where (Employee.OrganizationUnit = case when #OrganizationUnit=0 then 4 else #OrganizationUnit end OR case when #OrganizationUnit=0 then 5 else #OrganizationUnit end)
Try this, which should use indexes on your query...
DECALRE #FilterValues (FilterValue int not null primary key)
IF #Param=0
BEGIN
INSERT INTO #FilterValues VALUES (4)
INSERT INTO #FilterValues VALUES (5)
END
ELSE ID #PAram IS NOT NULL
BEGIN
INSERT INTO #FilterValues VALUES (#Param)
END
SELECT
....
FROM YourTable y
INNER JOIN #FilterValues f ON y.Value=f.Value
WHERE .....
KM's version will work, but this query does not need a temp table...
SELECT *
FROM Employee
WHERE (
#OrganizationUnit = 0
OR
(
#OrganizationUnit <> 0
AND
Employee.OrganizationUnit = #OrganizationUnit
)
)
How about
WHERE (Employee.ID = #EmpID OR #EmpID=0)
AND
(Employee.OrganizationUnit BETWEEN ISNULL(NULLIF(#OrganizationUnit,0),0) AND ISNULL(NULLIF(#OrganizationUnit,0),99))
AND NOT((EndDate < #StartDate or StartDate > #EndDate));