Cursor to fill data in table? - sql

CREATE TABLE [dbo].[Table_A](
[ID] [int] IDENTITY(1,1) NOT NULL,
[AREA] [varchar](50) NOT NULL,
[Year] [int] NOT NULL,
[Month] [int] NOT NULL,
[Factor] [float] NULL,
[Net] [float] NULL,
[Path] [varchar](255) NULL,
[Created] [smalldatetime] NULL,
[CreatedBy] [varchar](50) NOT NULL,
[LastModified] [varchar](50) NOT NULL,
[LastModifiedBy] [varchar](50) NOT NULL)
)
ALTER TABLE [dbo].[Table_A] ADD DEFAULT ((1.0)) FOR [Factor]
GO
ALTER TABLE [dbo].[Table_A] ADD DEFAULT (sysdatetime()) FOR [Created]
GO
ALTER TABLE [dbo].[Table_A] ADD DEFAULT (suser_name()) FOR [CreatedBy]
GO
ALTER TABLE [dbo].[Table_A] ADD DEFAULT (sysdatetime()) FOR [LastModified]
GO
ALTER TABLE [dbo].[Table_A] ADD DEFAULT (suser_name()) FOR [LastModifiedBy]
GO
I need to fill up the Table using cursor where
cursor should fetch data from
Declare Cur_AREA Cursor
For
Select Distinct Media From DIM_AREA
Note:DIM_AREA CONSISTS OF texas,dallas,chicago and newark.
Cursor should fill data for years 1990 to 2020
My cursor code is::
Declare #Temp_Year int
Select #Temp_Year = MAX(Year)
While #Temp_Year < DATEPART(YEAR, GETDATE())
Begin
Declare Cur_Media Cursor
For
Select Distinct Media From DIM_AREA
Order by Media
open Cur_Media
Fetch Next From Cur_Media
While ##FETCH_STATUS = 0
Begin
Declare #Temp_Month int
Set #Temp_Month = 1
While #Temp_Month <= 12
Begin
Insert into Table_A (Media, Year, month)
Set #Temp_Month = #Temp_Month + 1
Set #Temp_Year = #Temp_Year + 1
end
end
Close Cur_Media
Deallocate Cur_Media
But my cursor not working properly :(

You may have other problems here, I don't think your question is fleshed out, but you say:
Insert into Table_A (Media, Year, month)
But you don't pick anything to be inserted.
Use:
Insert into Table_A (Media, Year, month)
VALUES('','','')
or:
Insert into Table_A (Media, Year, month)
SELECT '','',''
FROM YourTable
Replacing the empty quotes with whatever you are trying to insert.

Related

Get Identity of destination table when using **DELETE FROM ... OUTPUT ... INTO**

I use bellow code to archive old data in ArchiveTable and delete archived data from SourceTable
DELETE FROM SourceTable
OUTPUT
DELETED.[ID],
DELETED.[Code],
DELETED.[Title]
INTO ArchiveTable([OldID], [Code], [Title])
WHERE Condition
Structure of tables:
CREATE TABLE [SourceTable](
[ID] [INT] IDENTITY(1,1) NOT NULL,
[Code] [VARCHAR](16) NULL,
[Title] [NVARCHAR](128) NULL,
CONSTRAINT [PK_SourceTable] PRIMARY KEY CLUSTERED ([ID] ASC)
)
GO
CREATE TABLE [ArchiveTable](
[ID] [INT] IDENTITY(1,1) NOT NULL,
[OldID] [INT] NOT NULL,
[Code] [VARCHAR](16) NULL,
[Title] [NVARCHAR](128) NULL,
CONSTRAINT [PK_ArchiveTable] PRIMARY KEY CLUSTERED ([ID] ASC)
)
GO
I need to return deleted records and ArchiveTable.[ID] to application. I change the code like this:
DELETE FROM SourceTable
OUTPUT
DELETED.[ID],
DELETED.[Code],
DELETED.[Title]
INTO ArchiveTable([OldID], [Code], [Title])
OUTPUT DELETED.*
WHERE Condition
This code return deleted records but I don't know how to get ID of ArchiveTable for this records. Look at ArchiveTable structure, It has OldID column that refer to SourceTable.ID and ID column that it is an Identity column of ArchiveTable. I need to ArchiveTable.ID in final result.
You can use a temporary table
CREATE TABLE #DeletedRows(
[ID] [INT] NOT NULL,
[Code] [VARCHAR](16) NULL,
[Title] [NVARCHAR](128) NULL
)
DELETE SourceTable
OUTPUT
DELETED.[ID],
DELETED.[Code],
DELETED.[Title]
INTO #DeletedRows([ID], [Code], [Title])
WHERE Condition
INSERT ArchiveTable([OldID], [Code], [Title])
OUTPUT INSERTED.*
SELECT [ID], [Code], [Title]
FROM #DeletedRows
DROP TABLE #DeletedRows
A variant with a table variable
DECLARE #DeletedRows TABLE(
[ID] [INT] NOT NULL,
[Code] [VARCHAR](16) NULL,
[Title] [NVARCHAR](128) NULL
)
DELETE SourceTable
OUTPUT
DELETED.[ID],
DELETED.[Code],
DELETED.[Title]
INTO #DeletedRows([ID], [Code], [Title])
WHERE Condition
INSERT ArchiveTable([OldID], [Code], [Title])
OUTPUT INSERTED.*
SELECT [ID], [Code], [Title]
FROM #DeletedRows
I found an interesting variant using DML with OUTPUT in SP and INSERT...EXEC... after that:
Test tables:
CREATE TABLE TestTable(
ID int NOT NULL PRIMARY KEY,
Title varchar(10) NOT NULL
)
CREATE TABLE TestTableLog(
LogID int NOT NULL IDENTITY,
OperType char(1) NOT NULL,
CHECK(OperType IN('I','U','D')),
ID int NOT NULL,
Title varchar(10) NOT NULL
)
DML procedures:
CREATE PROC InsTestTable
#ID int,
#Title varchar(10)
AS
INSERT TestTable(ID,Title)
OUTPUT inserted.ID,inserted.Title,'I' OperType
VALUES(#ID,#Title)
GO
CREATE PROC UpdTestTable
#ID int,
#Title varchar(10)
AS
UPDATE TestTable
SET
Title=#Title
OUTPUT inserted.ID,inserted.Title,'U' OperType
WHERE ID=#ID
GO
CREATE PROC DelTestTable
#ID int
AS
DELETE TestTable
OUTPUT deleted.ID,deleted.Title,'D' OperType
WHERE ID=#ID
GO
Tests:
-- insert test
INSERT TestTableLog(ID,Title,OperType)
EXEC InsTestTable 1,'A'
INSERT TestTableLog(ID,Title,OperType)
EXEC InsTestTable 2,'B'
INSERT TestTableLog(ID,Title,OperType)
EXEC InsTestTable 3,'C'
-- update test
INSERT TestTableLog(ID,Title,OperType)
EXEC UpdTestTable 2,'BBB'
-- delete test
INSERT TestTableLog(ID,Title,OperType)
EXEC DelTestTable 3
GO
-- show resutls
SELECT *
FROM TestTableLog
Maybe it'll be interesting to someone.

Cannot update a timestamp column

I was recommended the following stored procedure to audit a login table.
CREATE PROCEDURE ApplicationLogin
#Username NVARCHAR(255),
#IpAddress NVARCHAR(255)
AS
BEGIN
DECLARE #UserID INT;
BEGIN TRANSACTION;
SET #UserID = (SELECT UserID FROM User WHERE Username = #Username);
IF #UserID > 0
BEGIN
UPDATE User
SET LastLogin = GETDATE()
WHERE UserID = #UserID;
END
INSERT INTO UserLogger (Username, UserID, TimeStamp)
VALUES (#Username, #UserID, #Timestamp);
COMMIT TRANSACTION;
SELECT #UserID
END
However I can't make it work for some syntax errors that I can't figure out.
The User table looks like this:
CREATE TABLE [dbo].[User]
(
[UserID] [INT] IDENTITY(1,1) NOT NULL,
[UserName] [VARCHAR](50) NOT NULL,
[Enabled] [BIT] NOT NULL,
[LastLogin] [TIMESTAMP] NOT NULL,
PRIMARY KEY CLUSTERED ([UserID] ASC)
)
The Audit table looks like this:
CREATE TABLE [dbo].[UserLogger]
(
[UserID] [INT] IDENTITY(1,1) NOT NULL,
[UserName] [VARCHAR](50) NOT NULL,
[Name] [VARCHAR](100) NULL,
[TS] [TIMESTAMP] NULL,
[IpAddress] [NCHAR](10) NULL
) ON [PRIMARY]
I get an error
Cannot update a timestamp column
which I don't see why.
Any idea?
timestamp is not what you think it is. It is some sort of internal representation of the row address. Here is an explanation.
Use datetime or datetime2 to fix your problem.
This is made all the more confusing, because CURRENT_TIMESTAMP doesn't return a timestamp.

updated record is inserting into the history table not the old record

i have two tables Test and TestHistory
CREATE TABLE [dbo].[TEST](
[ID] [int] NULL,
[Name] [varchar](10) NULL,
[Status] [char](1) NULL,
[CreatedDate] [datetime] NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Test_History](
[ID] [int] NULL,
[Name] [varchar](10) NULL,
[Status] [char](1) NULL,
[CreatedDate] [datetime] NULL
) ON [PRIMARY]
GO
INSERT INTO TEST ([ID],[Name],[Status],[CreatedDate])values (1,'Mohan','A',GETDATE())
Created Trigger :
ALTER TRIGGER [dbo].[trg_Test]
ON [dbo].[TEST]
FOR UPDATE
AS
Declare #ID INT;
Declare #Name varchar(10);
Declare #Status CHAR(2);
Declare #CreatedDate DATETIME;
Select #ID = I.ID from INSERTED I
Select #Name = I.Name from INSERTED I
Select #Status = I.Status from INSERTED I
Select #CreatedDate = I.CreatedDate from INSERTED I
INSERT INTO [dbo].[Test_history]
([ID]
,[Name]
,[Status]
,[CreatedDate]
)
SELECT #ID,
#Name,
#Status,
GETDATE()
FROM INSERTED I
WHERE #ID = [ID]
When I'm updating the record like
Update [TEST] SET Status = 'I' then the old record with Status = 'A' should inserted but the what ever i'm updating it has been inserting into Testhistory table not the old record
where i'm doing wrong and how to insert old value
like if i updating Status = 'I' and in history table Status = 'A' shoul be inserted
You need to INSERT from DELETED not from INSERTED.
See examples here Understanding SQL Server inserted and deleted tables for DML triggers.
Like Karl mentioned, you need to refer deleted table for old updated row information. Apart from that your existing code doesn't work when you update more than a single row. What you need is something like this.
ALTER TRIGGER [dbo].[trg_Test]
ON [dbo].[TEST]
FOR UPDATE
AS
INSERT INTO [dbo].[Test_history]
(
[ID],
[Name],
[Status],
[CreatedDate]
)
SELECT ID,
Name,
Status,
GETDATE()
FROM deleted d

Unable to exit Sql cursor

This is hard for me to explain but I have tried my best to make it as easy for you all as I can.
I have two tables. One stores information about customer tbl_installations. Another his internet bills tbl_bills (this one is more important).
tbl_installations
CREATE TABLE [dbo].[tbl_installations](
[SrNo] [int] IDENTITY(1000,1) NOT NULL,
[name] [varchar](200) NULL,
[email] [varchar](100) NULL,
[phone] [varchar](20) NULL,
[plandesc] [varchar](50) NULL,
[plancost] [money] NULL,
[amtpaid] [money] NULL,
[amtbalance] [money] NULL,
[address] [varchar](500) NULL,
[referencename] [varchar](20) NULL,
[installationdate] [date] NULL,
[planduration] [int] NULL,
[MkrId] [varchar](20) NULL,
[MkrDt] [datetime] NULL,
[FromDate] [datetime] NULL,
[ToDate] [datetime] NULL,
[ExpiryDate] [datetime] NULL,
[Status] [varchar](20) NULL,
[CustomerId] [varchar](20) NULL,
[ServiceTax] [money] NULL,
[TotalAmt] [money] NULL
) ON [PRIMARY]
tbl_bills
CREATE TABLE [dbo].[tbl_Bills](
[Srno] [int] IDENTITY(1,1) NOT NULL,
[CustomerId] [varchar](20) NULL,
[PlanDuration] [int] NULL,
[Amount] [money] NULL,
[PaidAmt] [money] NULL,
[discount] [money] NULL,
[PlanDesc] [varchar](20) NULL,
[FromDate] [datetime] NULL,
[ToDate] [datetime] NULL,
[MkrId] [varchar](20) NULL,
[MkrDt] [datetime] NULL,
[ServiceTax] [money] NULL,
[TotalAmt] [money] NULL,
[PendingAmt] AS ([TotalAmt]-([PaidAmt]+[discount])),
[PaidStatus] [varchar](10) NULL
) ON [PRIMARY]
THIS SCRIPT WILL GIVE YOU DATA THAT I HAVE:
insert into tbl_installations(name,email,phone,plandesc,plancost,amtpaid,amtbalance,address,referencename,installationdate,planduration,MkrId,
MkrDt,FromDate,ToDate,ExpiryDate,Status,CustomerId,ServiceTax,TotalAmt)
values('Gulzar','asdasd','3242342',null,null,null,null,'asda asd aada','ref name',GETDATE()-4,1,'arbaaz', GETDATE(),null,null,
null,'INSTALLED','C2002',null,null )
insert into tbl_bills (PlanDuration,Amount,PaidAmt,discount,PlanDesc,FromDate,ToDate,PaidStatus,MkrId,MkrDt,ServiceTax,TotalAmt,CustomerId)
values(1,800,600,0,'1MB',GETDATE()-4,DATEADD(month,1, GETDATE()-4),'PAID','Arbaaz',getdate(),800*(12.36/100),800+800*(12.36/100),'C2001')
PendingAmt in tbl_bills is a computer column which calculates the pending amount based on totalamt - (paidamt+discount)
tbl_bills.Amount is the subsription cost eg: 800
tbl_bills.TotalAmt is sum of Amount+ServiceTax
Scenario
First Month:
Customer's subsription cost(Amount) is 800
PaidAmt=600
ServiceTax=98.88
TotalAmt=898.88
PendingAmt=298.88(computed field)
Second Month:
Customer still has same subscription worth 800 (total 898.88)
But he pays 1100 so that he settles some of pending amount.
This 1100 must first settle all the previous pending amounts first(by updating tbl_bills.paidamt and then what ever is left should be inserted as a brand new row in the same table.
HERE IS MY FAILED ATTEMPT :
THIS KEEPS ON RUNNING ENDLESSLY
declare #srno as int=0
declare #Cid as varchar(20)='C2001'
declare #PlanDuration as int=1
declare #Amount as money=800
declare #PlanDesc as varchar(20)='1MB'
declare #FromDate as datetime ='2014-11-11'
declare #ToDate as datetime='2014-12-11'
declare #PaidStatus as varchar(20)
declare #MkrId as varchar(20)='arbaaz'
declare #status as varchar(20)='PAID'
declare #Discount as money=0
declare #PaidAmt as money=1100
--as
if(#status='INSTALLED')
BEGIN
insert into tbl_bills (PlanDuration,Amount,PaidAmt,discount,PlanDesc,FromDate,ToDate,PaidStatus,MkrId,MkrDt,ServiceTax,TotalAmt,CustomerId)
values(#PlanDuration,#Amount,#paidamt,#discount,#plandesc,#FromDate,#ToDate,'PAID','Arbaaz',getdate(),#Amount*(12.36/100),#Amount+#Amount*(12.36/100),#Cid)
if((select sum(PendingAmt) from tbl_Bills where CustomerId=#cid)=0 )
begin
update tbl_installations set Status='PAID' ,ExpiryDate=#ToDate where CustomerId=#Cid
end
else
begin
update tbl_installations set Status='UNPAID' ,ExpiryDate=#ToDate where CustomerId=#Cid
end
END
ELSE
BEGIN
declare #pAmt as money
select #pAmt=sum(PendingAmt) from tbl_Bills where CustomerId=#Cid
declare #Amt as money =#Amount
select #Amt=#Amt
--select srno from tbl_Bills where CustomerId=#Cid and PendingAmt<>0 order by ToDate
if(#pAmt>0)
BEGIN------------------------
DECLARE #ColExpir datetime
DECLARE #ColFallprotec datetime
DECLARE #CurSrno int
--------------------------------------------------------
DECLARE #MyCursor CURSOR
SET #MyCursor = CURSOR FAST_FORWARD
FOR
select srno from tbl_Bills where CustomerId=#Cid and PendingAmt<>0 order by todate
OPEN #MyCursor
FETCH NEXT FROM #MyCursor
INTO #CurSrno
WHILE ##FETCH_STATUS = 0
BEGIN
IF(#Amt>0)
BEGIN
declare #PendingAtCurSrno money
select #PendingAtCurSrno=pendingamt from tbl_Bills where SrNo=#CurSrno
print #CurSrno -----
print #PendingAtCurSrno ----
if(#Amt>#PendingAtCurSrno)
begin
update tbl_Bills set PaidAmt=TotalAmt where SrNo=#CurSrno
select #Amt=#Amt-#PendingAtCurSrno
print '1st'----
print #amt----
if(#amt=0)
begin
CLOSE #MyCursor --tried break and return here too
DEALLOCATE #MyCursor --
end
end
else
begin
update tbl_Bills set PaidAmt=paidamt+#Amt where SrNo=#CurSrno
select #Amt=0
print '2nd'
print #amt----
if(#amt=0)
begin
CLOSE #MyCursor --tried break and return here too
DEALLOCATE #MyCursor
end
end
END
END
FETCH NEXT FROM #MyCursor
INTO #PendingAtCurSrno
END
CLOSE #MyCursor
DEALLOCATE #MyCursor
END-------------------------
if(#Amt>0)
begin
insert into tbl_bills (PlanDuration,Amount,PaidAmt,discount,PlanDesc,FromDate,ToDate,PaidStatus,MkrId,MkrDt,ServiceTax,TotalAmt,CustomerId)
values(#PlanDuration,#Amount,#Amt,#discount,#plandesc,#FromDate,#ToDate,'PAID','Arbaaz',getdate(),#Amount*(12.36/100),#Amount+#Amount*(12.36/100),#Cid)
end
if((select sum(PendingAmt) from tbl_Bills where CustomerId=#cid)=0 )
begin
update tbl_installations set Status='PAID' ,ExpiryDate=#ToDate where CustomerId=#Cid
end
else
begin
update tbl_installations set Status='UNPAID' ,ExpiryDate=#ToDate where CustomerId=#Cid
end
Not only does it keep running endlessly , it keeps updating the paidamt over and over again and never gets to the point where it should insert another row with remaining amount.

Inserted clause returns 0 when used with triggers

I'm trying to get the last inserted rows Id from an inserts statement on the following table using SQL server 2012
[dbo].[Table](
[TableId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[CreatedBy] [nvarchar](50) NULL,
[CreatedDate] [datetime2](7) NOT NULL,
[ModifiedBy] [nvarchar](50) NULL,
[ModifiedDate] [datetime2](7) NULL,
CONSTRAINT [pk_Table] PRIMARY KEY CLUSTERED
(
[TableId] ASC
)
I'm also using an audit triggers on that table that are as follows:
trigger [dbo].[trigger_Table_auditColumnAutoInsert]
on [dbo].[Table]
instead of insert
/**************************************************************
* INSTEAD OF trigger on table [dbo].[Table] responsible
for automatically inserting audit column data
**************************************************************/
as
begin
set nocount on
declare #currentTime datetime2
set #currentTime = GETUTCDATE()
insert into [dbo].[Table]
(
Name,
CreatedBy,
CreatedDate,
ModifiedBy,
ModifiedDate
)
select
Name,
ISNULL(CreatedBy, system_user),
#currentTime,
NULL,
NULL
from inserted
select SCOPE_IDENTITY() as [TableId]
goto EOP -- end of procedure
ErrorHandler:
if (##trancount <> 0) rollback tran
EOP:
end
I used different approaches, but nothing 'SAFE' seems to work.
Using scope identity returns null
insert into dbo.[Table](Name) Values('foo')
select SCOPE_IDENTITY()
Using OUTPUT INSERTED always returns 0 for the identity coloumns; although it returns the other inserted values:
declare #tmpTable table
(
TableId int,
Name nvarchar (50)
)
INSERT INTO [dbo].[Table]([Name])
output inserted.TableId, inserted.Name into #tmpTable
VALUES('foo')
select * from #tmpTable
TableId Name
0 foo
I know of another solution to get the inserted Id from the triggers itself, by executing a dynamic sql command as follows:
declare #tmpTable table (id int)
insert #tmpTable (id )
exec sp_executesql N'insert into dbo.[Table](Name) Values(''foo'')'
select id from #tmpTable
I couldn't figure out why in the first 2 cases it is not working; why the SCOPE_IDENTITY() does not work although the triggers execute in the same transaction? And also why the INSERTED clause returns 0 for the identity column.
It appears that the following requirements apply to your audit column data:
Use the insert value supplied for CreatedBy, or use SYSTEM_USER by default.
Always use GETUTCDATE() for CreatedDate.
If the INSTEAD OF trigger (rather than an AFTER trigger) is not essential to your requirements, then you can use DEFAULT constraints on your audit columns and an AFTER INSERT trigger to enforce requirement #2.
CREATE TABLE [dbo].[Table]
(
[TableId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[CreatedBy] [nvarchar](50) NOT NULL CONSTRAINT [DF_Table_CreatedBy] DEFAULT SYSTEM_USER,
[CreatedDate] [datetime2](7) NOT NULL CONSTRAINT [DF_Table_CreatedDate] DEFAULT GETUTCDATE(),
[ModifiedBy] [nvarchar](50) NULL,
[ModifiedDate] [datetime2](7) NULL,
CONSTRAINT [pk_Table] PRIMARY KEY CLUSTERED ([TableId] ASC)
)
GO
CREATE TRIGGER Trigger_Table_AfterInsert ON [dbo].[Table]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON
UPDATE [dbo].[Table] SET [CreatedDate]=GETUTCDATE()
FROM [dbo].[Table] AS T
INNER JOIN INSERTED AS I ON I.[TableId]=T.[TableId]
END
GO
Then, both SCOPE_IDENTITY() and OUTPUT INSERTED techniques to get the new TableId value work as expected.
If the INSTEAD OF trigger is essential to your implementation, then SELECT ##IDENTITY is an alternative to SCOPE_IDENTITY.