I have a table called Transaction_tbl with more than 400 000 records in it. This is the table structure:
CREATE TABLE [dbo].[Transaction_tbl](
[transactID] [numeric](18, 0) IDENTITY(1,1) NOT NULL,
[TBarcode] [varchar](20) NULL,
[cmpid] [int] NULL,
[Locid] [int] NULL,
[PSID] [int] NULL,
[PCID] [int] NULL,
[PCdID] [int] NULL,
[PlateNo] [varchar](20) NULL,
[vtid] [int] NULL,
[Compl] [bit] NULL,
[self] [bit] NULL,
[LstTic] [bit] NULL,
[Gticket] [int] NULL,
[Cticket] [int] NULL,
[Ecode] [varchar](50) NULL,
[dtime] [datetime] NULL,
[LICID] [int] NULL,
[PAICID] [int] NULL,
[Plot] [varchar](50) NULL,
[mkid] [int] NULL,
[mdlid] [int] NULL,
[Colid] [int] NULL,
[Comments] [varchar](100) NULL,
[Kticket] [int] NULL,
[PAmount] [numeric](18, 2) NULL,
[Payid] [int] NULL,
[Paid] [bit] NULL,
[Paydate] [datetime] NULL,
[POICID] [int] NULL,
[DelDate] [datetime] NULL,
[DelEcode] [nvarchar](50) NULL,
[PAICdate] [datetime] NULL,
[KeyRoomDate] [datetime] NULL,
[Status] [int] NULL,
CONSTRAINT [PK_Transaction_tbl] PRIMARY KEY CLUSTERED
(
[transactID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
I have a nonclustered index on the Locid, dtime column.
I have a stored procedure like this:
ALTER procedure [dbo].[IBS_fetchreleasedinpodiumgridnew]
#locid INTEGER = NULL
AS BEGIN
SET NOCOUNT ON
DECLARE #TodayMinus7Days DATETIME
Declare #krrt integer
Declare #DT integer
SET #TodayMinus7Days = getdate()-1
SELECT
t.TBarcode, t.PlateNo, t.DelEcode,cast(t.Paydate as Time) [REQ],
datediff(MINUTE, t.PayDate,
CASE t.Status
WHEN 3 THEN GETDATE()
WHEN 4 THEN t.KeyRoomDate
When 5 THEN t.KeyRoomDate
End) as KRRT,
datediff(MINUTE,t.PayDate,
CASE t.Status
WHEN 3 THEN GETDATE()
WHEN 4 THEN GETDATE()
WHEN 5 THEN t.DelDate
END) as DT
FROM
dbo.Transaction_tbl t
WHERE
(
([status] IN (3,4))
OR
([status] = 5 AND DATEDIFF(n, DelDate, GETDATE()) <= 3)
)
AND locid = 6 AND dtime >= #TodayMinus7Days
ORDER BY
paydate
end
my execution plan like this:
but most of the time this is taking long time to execute ..in this case how i can improve my stored procedure execution performance?
any other method i want to use..any help is very appriciable.Thanks
the query execution plan is showing sorting is taking long time..so if i give index on paydate my query performance will increase?
instead of this dtime >= #TodayMinus7Days i given code like this:
dtime >= OPTION (optimize for (#TodayMinus7Days))
but getting error:Incorrect syntax near the keyword 'OPTION'.
Apart from optimizing the query, there are a couple things I can immediately suggest to improve the performance of a stored procedure.
Parameter sniffing: When the store procedure is passed a parameter, it analyses the dataset to figure out what would be the most efficient indexes. This is useful, though the plan is cached, and will get out of date, causing the stored proc to run on an inefficient execution plan.
Solution: Re-declare parameters or optimize the stored proc for unknown parameter values
Suppress the row count: One of the most simple things you can do to increase the performance of a stored procedure is SET NOCOUNT ON. This will prevent SQL Server from sending messages back to the client after the execution of each statement, which is not required for a stored proc. It seems like a small improvement, but the results are noticeable.
Solution: SET NOCOUNT ON
The snippet below has an example of where they go in your stored proc. Note that if you are re-declaring parameters, you don't need to optimize for unknown, and vice versa.
CREATE PROCEDURE dbo.example_proc
(
#USER_PARAM VARCHAR(200)
)
AS
BEGIN
-- suppress the number of rows returned
SET NOCOUNT ON;
-- Re-declaring the variable will prevent paramater sniffing
DECLARE #LOCAL_USER_PARAM VARCHAR(200) = #USER_PARAM
SELECT
*
FROM some_table st
WHERE st.some_column = #LOCAL_USER_PARAM
-- If you don't re-declare params, you can add this line
OPTION (OPTIMIZE FOR (#USER_PARAM UNKNOWN))
--
END
GO
Try using "with recompile" to get more suitable execution plan based on parameter. In my case it help reduce time from about 1 hours to a few minutes. Hope it work in your case as well.
exec [dbo].[IBS_fetchreleasedinpodiumgridnew] 1 with recompile
Check By Manually run the SP. If it is working fine than your problem is isolation level.
add these line in store procedure beginning.
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
Related
I'm trying to script sp_WhoIsActive into a table. The goal is feed the table with an Agent Job every 10 seconds.
I followed this guide and I tried to feed the table this way:
--Log activity into table.
DECLARE #destination_table VARCHAR(4000) =
'[Monitoring].[dbo].[WhoIsActive] '
EXEC sp_WhoIsActive
#get_plans = 1,
#get_transaction_info = 1,
#destination_table = #destination_table;
But as result I receive the error:
Warning: The join order has been enforced because a local join hint is used.
Msg 50000, Level 16, State 1, Procedure sp_WhoIsActive, Line 1111 [Batch Start Line 0]
Destination table not properly formatted.
On Google I found many guides talking about a solution that could help me execute a Stored Procedure into a temp table and from there I could create a table:
sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO
sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO
SELECT * INTO #MyTempTable FROM OPENROWSET('SQLNCLI', 'Server=localhost;Trusted_Connection=yes;',
'EXEC sp_WhoIsActive')
SELECT * FROM #MyTempTable
But this process too is failing with error:
Msg 11526, Level 16, State 1, Procedure sys.sp_describe_first_result_set, Line 1 [Batch Start Line 12]
The metadata could not be determined because statement 'INSERT #sessions
(
recursion,
session_id,
request_id' in procedure 'sp_WhoIsActive' uses a temp table.
I tried following Kendra Little blog but that too is not working.
In the end I scripted out the table manually:
CREATE TABLE [dbo].[WhoIsActive](
[dd_hh_mm_ss_mss] [nvarchar](50) NOT NULL,
[session_id] [tinyint] NOT NULL,
[sql_text] [nvarchar](max) NOT NULL,
[sql_command] [nvarchar](400) NOT NULL,
[login_name] [nvarchar](50) NOT NULL,
[wait_info] [nvarchar](50) NOT NULL,
[tran_log_writes] [nvarchar](50) NOT NULL,
[CPU] [smallint] NOT NULL,
[tempdb_allocations] [smallint] NOT NULL,
[tempdb_current] [smallint] NOT NULL,
[blocking_session_id] [nvarchar](50) NOT NULL,
[reads] [int] NOT NULL,
[writes] [float] NOT NULL,
[physical_reads] [tinyint] NOT NULL,
[query_plan] [nvarchar](50) NOT NULL,
[used_memory] [tinyint] NOT NULL,
[status] [nvarchar](50) NOT NULL,
[tran_start_time] [datetime2](7) NOT NULL,
[implicit_tran] [nvarchar](50) NOT NULL,
[open_tran_count] [tinyint] NOT NULL,
[percent_complete] [nvarchar](50) NOT NULL,
[host_name] [nvarchar](50) NOT NULL,
[database_name] [nvarchar](50) NOT NULL,
[program_name] [nvarchar](100) NOT NULL,
[start_time] [datetime2](7) NOT NULL,
[login_tine] [datetime2](7) NOT NULL,
[request_id] [tinyint] NOT NULL,
[collection_time] [datetime2](7) NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
But that too is failing and I cannot feed the table with a job.
sp_WhoIsActive is so popular, I cannot believe I'm the only one trying to insert the results into a table.
You need to create a table suitable to use with the output of the procedure, the schema of which can vary depending on the options you use.
SP_WhoIsActive will actually give you the create script, so to capture the default options just do
declare #definition varchar(max)
exec sp_WhoIsActive #return_schema = 1, #schema = #definition output
print #definition
This returns the appropriate T-SQL:
CREATE TABLE < table_name > (
[dd hh:mm:ss.mss] VARCHAR(8000) NULL
,[session_id] SMALLINT NOT NULL
,[sql_text] XML NULL
,[login_name] NVARCHAR(128) NOT NULL
,[wait_info] NVARCHAR(4000) NULL
,[CPU] VARCHAR(30) NULL
,[tempdb_allocations] VARCHAR(30) NULL
,[tempdb_current] VARCHAR(30) NULL
,[blocking_session_id] SMALLINT NULL
,[reads] VARCHAR(30) NULL
,[writes] VARCHAR(30) NULL
,[physical_reads] VARCHAR(30) NULL
,[used_memory] VARCHAR(30) NULL
,[status] VARCHAR(30) NOT NULL
,[open_tran_count] VARCHAR(30) NULL
,[percent_complete] VARCHAR(30) NULL
,[host_name] NVARCHAR(128) NULL
,[database_name] NVARCHAR(128) NULL
,[program_name] NVARCHAR(128) NULL
,[start_time] DATETIME NOT NULL
,[login_time] DATETIME NULL
,[request_id] INT NULL
,[collection_time] DATETIME NOT NULL
)
Edit and run with the required target table name, then you can run sp_whoisactive with the destination table option
exec sp_WhoIsActive #destination_table='Monitoring.dbo.WhoIsActive'
See the docs
In my database I have about 10 tables connected in one central table (Mobile). This table (Mobile) has a column called price which is the sum of the prices of all other nested tables. I would like that when price of another table (like Battery, Camera, ...) is updated, the price of the central table (Mobile) would be updated too.
I will show the schema of central table and two more (for reducing code, other nested tables are so similar)
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[table_mobile]
(
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [varchar](50) NOT NULL,
[processor] [int] NOT NULL,
[memory_ram] [int] NOT NULL,
[memory_rom] [int] NOT NULL,
[operating_system] [int] NOT NULL,
[graphic] [int] NOT NULL,
[screen] [int] NOT NULL,
[battery] [int] NOT NULL,
[camera] [int] NOT NULL,
[material] [int] NOT NULL,
[extra] [int] NOT NULL,
[price] [decimal](18, 2) NOT NULL,
[created_by] [int] NOT NULL,
[created_at] [timestamp] NOT NULL,
CONSTRAINT [PK_mobiles]
PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[table_battery]
(
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [varchar](50) NOT NULL,
[capacity] [int] NOT NULL,
[description] [varchar](250) NOT NULL,
[image] [image] NOT NULL,
[price] [decimal](18, 2) NOT NULL,
CONSTRAINT [PK_table_battery]
PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[table_camera]
(
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [varchar](50) NOT NULL,
[megapixels] [int] NOT NULL,
[description] [varchar](250) NOT NULL,
[image] [image] NOT NULL,
[price] [decimal](18, 2) NOT NULL,
CONSTRAINT [PK_table_camera]
PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
As I say, I think that my purpose should be achieve with a trigger but any other suggest is accepted.
I'll show you what I want to do by programming in C#:
table_mobile.price = table_battery.price + table_camera.price + ... + table_XXX.price
Any idea how can I achive my trouble?
Thank you.
EDIT 1:
Using SSMS... I have created this template for a Trigger:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER <Schema_Name, sysname, Schema_Name>.<Trigger_Name, sysname, Trigger_Name>
ON <Schema_Name, sysname, Schema_Name>.<Table_Name, sysname, Table_Name>
AFTER <Data_Modification_Statements, , INSERT,DELETE,UPDATE>
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for trigger here
END
GO
I have not worked with SQL Server for a while, so forgive me if there are any typos, but basically you will need to create a trigger for each of the tables linked to mobile and add the difference of the new and the old value to the price of the mobile:
create trigger PriceChange on table_battery
after update
as
BEGIN
update table_mobile
set price = table_mobile.price + i.price
from table_mobile
inner join INSERTED i
on table.mobile.id = i.id;
update table_mobile
set price = table_mobile.price - d.price
from table_mobile
inner join DELETED d
on table.mobile.id = d.id;
END
Note that we do separate updates, because the id might have changed. If the id stays the same, then you can use a single update with a difference. The code is untested, so if there are any problems, then please, let me know.
EDIT
You may also do this from application level where you trigger the updates. After any such update you can run an update for table_mobile, adding the values. The benefit would be that you can do the calculation only once if you know that several prices for the same mobiles will be changed.
EDIT2
Apparently this command should be used inside the trigger:
UPDATE [dbo].[table_mobile]
SET price = table_mobile.price + i.price - d.price
FROM [dbo].[table_mobile],
INSERTED i,
DELETED d
WHERE battery = d.id
This is an example of the view I mentioned:
create view MobileWithPriceAggregate as
select [id]
, [name]
, [processor]
, [memory_ram]
, [memory_rom]
, [operating_system]
, [graphic]
, [screen]
, [battery]
, [camera]
, [material]
, [extra]
, price = m.price+b.price+c.price
from [table_mobile] m
join [table_battery] b on b.id=m.battery
join [table_camera] c on c.id=m.camera
Note: if not all Mobiles have a camera, then you need to use left join and null handle like ISNULL(c.price,0)
I have a Sql Azure database, everything was good from last 6 months until today, when a simple
Delete from ListData where ListID=2323
fail to delete 7500 records out of 1.9 Million records after 14 minutes of running query. However Select query take less than 2-3 second to list them all.
Previously the delete works much like select and it usually take less than 20 second to finish delete operation. something is wrong today only.
Complete database size is 1.1GB where as we have Web edition set at 5GB so, we have plenty of space available.
Any idea what is going wrong? that delete has cause some serious problem in system which cause my client lose quite a money.
Thanks for any guide.
Edit: I do have couple of Index on table, but no trigger, FK or any other such thing in table. LISTID is foreign key [logically], and RecordID [another column in table] is auto increment id in Listdata table.
*Edit 2 *:
/****** Object: Table [dbo].[tblSalesListData] Script Date: 02-07-2013 11:45:14 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[ListData](
[RecordID] [bigint] IDENTITY(1,1) NOT NULL,
[ListID] [bigint] NULL,
[SalesID] [bigint] NULL,
[UserID] [varchar](100) NULL,
[FirstName] [varchar](100) NULL,
[MiddleName] [varchar](50) NULL,
[LastName] [varchar](50) NULL,
[Address1] [varchar](100) NULL,
[Address2] [varchar](100) NULL,
[City] [varchar](100) NULL,
[State] [varchar](100) NULL,
[ZipCode] [varchar](10) NULL,
[WorkPhone] [varchar](15) NULL,
[HomePhone] [varchar](15) NULL,
[CellPhone] [varchar](15) NULL,
[Email] [varchar](100) NULL,
[DealerCode] [varchar](20) NULL,
[IsPrinted] [varchar](10) NULL,
[tag] [varchar](100) NULL,
[RecordDate] [datetime] NULL,
[CustomInfo] [text] NULL,
[SourceData] [text] NULL,
CONSTRAINT [PK_ListData] PRIMARY KEY CLUSTERED
(
[RecordID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[ListData] ADD DEFAULT ('N') FOR [IsPrinted]
GO
Try to add index and change table structure -
CREATE TABLE dbo.tblSalesListData
(
RecordID BIGINT IDENTITY(1,1) NOT NULL PRIMARY KEY,
ListID BIGINT NOT NULL, -- NULL --> NOT NULL
SalesID BIGINT NULL,
UserID VARCHAR(100) NULL,
FirstName VARCHAR(100) NULL,
MiddleName VARCHAR(50) NULL,
LastName VARCHAR(50) NULL,
Address1 VARCHAR(100) NULL,
Address2 VARCHAR(100) NULL,
City VARCHAR(100) NULL,
[State] VARCHAR(100) NULL,
ZipCode VARCHAR(10) NULL,
WorkPhone VARCHAR(15) NULL,
HomePhone VARCHAR(15) NULL,
CellPhone VARCHAR(15) NULL,
Email VARCHAR(100) NULL,
DealerCode VARCHAR(20) NULL,
IsPrinted VARCHAR(10) NULL,
tag VARCHAR(100) NULL,
RecordDate DATETIME NULL,
CustomInfo VARCHAR(MAX) NULL, -- TEXT --> VARCHAR(MAX)
SourceData VARCHAR(MAX) NULL -- TEXT --> VARCHAR(MAX)
)
GO
CREATE /*UNIQUE*/ NONCLUSTERED INDEX IX_ListID ON dbo.tblSalesListData
(
ListID ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
I have a little bit of same problem. I did the following step. but be sure to back up the database before doing these steps.
Create another table with identical structure
Insert the data in new table of the old table
Drop the old table
and try again to see how much time will it take.
Thanks for #Dinup and #Devart that give me idea, though I didn't fully follow their word, but both of them guide me in direction where I found solution as :
Delete all Indexes based on my ListID.
Run my Query, it take less than 1 second now.
Recreate my indexes.
Happy Living.
I'm fairly new to SQL Server any input and advice would help greatly.
I have 3 tables which are in one-to-many relationships.
Table Person holds customer info
CREATE TABLE [dbo].[Person](
[PID] [int] IDENTITY(1,1) NOT NULL,
[FirstName] [varchar](255) NULL,
[LastName] [varchar](255) NULL,
[CAddress] [varchar](255) NULL,
[Ccity] [varchar](255) NULL,
[Cstate] [varchar](2) NULL,
[Czipcode] [varchar](20) NULL,
[Ccountry] [varchar](255) NULL,
[Cphone] [varchar](25) NULL,
[Cemail] [varchar](255) NULL,
[CipAddress] [varchar](255) NULL)
Table Transaction holds their transaction
CREATE TABLE [dbo].[Transaction](
[TID] [int] IDENTITY(1,1) NOT NULL,
[PID] [int] NOT NULL,
[DateOfTransaction] [date] NULL)
with a third table, TransactionDetail, which holds transaction details
CREATE TABLE [dbo].[TransactionDetail](
[TDID] [int] IDENTITY(1,1) NOT NULL,
[TID] [int] NULL,
[ProductID] [int] NULL,
[ProductName] [varchar](255) NULL,
[ProductQTY] [int] NULL,
[ProductPrice] [decimal](18, 2) NULL)
I would like to create a stored procedure to insert once into the Person table then insert multiple details into the third table.
this is what i got i'm not sure if this is correct?
CREATE TYPE dbo.TransactionTableType AS TABLE
( TID int, ProductID int, ProductName varchar(255), ProductQTY int, ProductPrice decimal(18,2) )
go
CREATE PROCEDURE insertTransacion
#NewProduct dbo.TransactionTableType READONLY,
#FirstName varchar(255),
#LastName varchar(255),
#CAddress varchar(255),
#Ccity varchar(255),
#Cstate varchar(2),
#Czipcode varchar(20),
#Ccountry varchar(255),
#CPhone varchar(25),
#Cemail varchar(255),
#CipAddress varchar(255),
#DateOfTrans date
as
begin
SET NOCOUNT ON;
DECLARE #Pid int
insert into Person(FirstName,LastName,CAddress,Ccity,Cstate,Czipcode,Ccountry,Cphone,Cemail,CipAddress) values (#FirstName,#LastName,#CAddress,#Ccity,#Cstate,#Czipcode,#Ccountry,#CPhone,#Cemail,#CipAddress)
SET #Pid = SCOPE_IDENTITY()
insert into PTransactions(PID, DateOfTransaction) values (#Pid, #DateOfTrans)
DECLARE #Tid int
SET #Tid = SCOPE_IDENTITY()
insert into TransactionDetail(TID, ProductID, ProductName, ProductQTY, ProductPrice) Select #Tid, ntd.ProductID, ntd.ProductName, ntd.ProductQTY, ntd.ProductPrice from #NewProduct as ntd
end
Not sure how to do this in a stored procedure I know how to do it programmatically in asp.net using ado, however I'm trying to avoid that. Sorry for the grammar.
Short answer is you can't, although there are a couple of options open to you.
You can either create a SP to enter the person data, and a separate one to insert the data - one line at a time. Return the person id value with the first call and use that in the subsequent SP calls to insert the data. If you go down this path, make sure you wrap the calling code up in transaction objects so you can roll back the whole lot if you have a problem. You don't state what language you're using for the rest of your code?
Second option is to look at the SQL Bulk Insert command - this is best if you have a lot of data to add into the third table. But involves writing that data out to a file first - slight pain but it's then very fast. Very good if you have thousands or more rows to add.
Couple of other options out there as well depending on your development language.
Cheers
Simon
Long time reader, first time caller, and all that...
Here's the business problem: a user makes one or more requests for documents. At some later time, a document is uploaded to the system. If that document matches one or more of the requests, the document request is fulfilled. So, there could be one or many requests for Document A, for instance. When Document A is uploaded, all of the requests for Document A are fulfilled.
And here's my technical problem: I've got an AFTER INSERT trigger on the table where the upload of the document is recorded. It checks the DocumentRequest table and updates each row that matches the uploaded document. If only one row matches (only one request for Document A), everything is hunky-dory. If, however, more than one matches, the UPDATE statement in the trigger fails -- I see this error:
Subquery returned more than 1 value.
This is not permitted when the
subquery follows =, !=, <, <= , >, >=
or when the subquery is used as an
expression.
Here are the CREATE statements for the relevant tables:
CREATE TABLE [dbo].[DocumentRequest](
[DocumentRequestId] [int] IDENTITY(1,1) NOT NULL,
[BatchID] [int] NULL,
[CertificateNum] [varchar](20) NOT NULL,
[RequestedTypCd] [varchar](5) NULL,
[RequestedReason] [varchar](60) NULL,
[DestinationTypCd] [varchar](5) NULL,
[DocumentPackageTypCd] [varchar](5) NULL,
[RequestedDtm] [datetime] NOT NULL,
[RequestNotes] [varchar](1000) NULL,
[RequestStatusTypCd] [varchar](5) NOT NULL,
[InactiveFlag] [char](1) NULL,
[CreationDtm] [datetime] NOT NULL,
[CreationUserID] [varchar](10) NOT NULL,
[CompletedDtm] [datetime] NULL,
[CompletedUserID] [varchar](10) NULL,
[ModifiedDtm] [datetime] NULL,
[ModifiedUserID] [varchar](10) NULL,
CONSTRAINT [XPKDocumentRequest] PRIMARY KEY NONCLUSTERED
(
[DocumentRequestId] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
--------------
CREATE TABLE [dbo].[DocumentRequestContents](
[DocumentTypCd] [varchar](5) NOT NULL,
[CreationDtm] [datetime] NOT NULL,
[CreationUserID] [varchar](10) NOT NULL,
[DocumentRequestId] [int] NOT NULL,
[DocumentReceivedDtm] [datetime] NULL,
[DocumentReceivedFlag] [char](1) NULL,
[DocumentIgnoreFlag] [char](1) NULL,
CONSTRAINT [XPKDocumentRequestContents] PRIMARY KEY NONCLUSTERED
(
[DocumentRequestId] ASC,
[DocumentTypCd] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
--------------
CREATE TABLE [dbo].[DocumentStorage](
[DocumentStorageID] [int] IDENTITY(1,1) NOT NULL,
[CertificateNum] [varchar](10) NULL,
[DocumentHandleID] [int] NULL,
[DocumentTypVal] [varchar](100) NULL,
[CreationDtm] [datetime] NULL,
[CreationUserID] [varchar](10) NULL,
[lastupd_user] [varchar](8) NULL,
[lastupd_stamp] [datetime] NULL,
[DocumentNam] [varchar](100) NULL,
PRIMARY KEY NONCLUSTERED
(
[DocumentStorageID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
--------------
CREATE TABLE [dbo].[DocumentProfile](
[DocumentProfileID] [int] IDENTITY(1,1) NOT NULL,
[DocumentTypCd] [varchar](5) NULL,
[DocumentVersionNum] [varchar](10) NULL,
[DocumentApproveInd] [varchar](1) NULL,
[CreationDtm] [datetime] NULL,
[CreationUserID] [varchar](10) NULL,
[lastupd_stamp] [datetime] NULL,
[lastupd_user] [varchar](8) NULL,
[DocumentStorageTypVal] [varchar](100) NULL,
[MIMETypVal] [varchar](50) NULL,
[DocumentSourceTypCd] [varchar](5) NULL,
[DocumentFormatTypCd] [varchar](5) NULL,
[DocumentReceiveLocationTypCd] [varchar](5) NULL,
[DocumentIndexTypCd] [varchar](5) NULL,
[DocumentNam] [varchar](100) NULL,
[DocumentTrackedInd] [char](1) NULL CONSTRAINT [DF_DocumentProfile_DocumentTrackedInd] DEFAULT ('N'),
PRIMARY KEY NONCLUSTERED
(
[DocumentProfileID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
And the trigger...
CREATE TRIGGER [trg_DMTDocumentReceived_DocumentStorage]
ON [dbo].[DocumentStorage]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
UPDATE DocumentRequestContents
SET DocumentRequestContents.DocumentReceivedFlag = 'Y',
DocumentRequestContents.DocumentReceivedDtm = getdate()
FROM
DocumentRequestContents
INNER JOIN DocumentRequest
ON DocumentRequestContents.DocumentRequestId = DocumentRequest.DocumentRequestId
INNER JOIN DocumentProfile
ON DocumentRequestContents.DocumentTypCd = DocumentProfile.DocumentTypCd
INNER JOIN Inserted
ON DocumentProfile.DocumentStorageTypVal = Inserted.DocumentTypVal
WHERE
(DocumentRequestContents.DocumentReceivedFlag <> 'Y' OR
DocumentRequestContents.DocumentReceivedFlag IS NULL)
AND (DocumentRequest.InactiveFlag IS NULL)
AND (DocumentRequest.CertificateNum = Inserted.CertificateNum)
AND (DocumentProfile.DocumentStorageTypVal = Inserted.DocumentTypVal)
END
I'm at a bit of a loss as to where to go from here. Can you give a guy a hand?
EDIT:
A request can have more than one document, so the table that is being updated in the trigger (DocumentRequestContents) also has a trigger on it, which determines if the entire request has been fulfilled. As far as I can tell, it does not have a subquery, either, but here it is:
CREATE TRIGGER [trg_DMTDocumentReceived_CompletenessCheck]
ON [dbo].[DocumentRequestContents]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #TotalDocs int, #ReceivedDocs int
IF UPDATE(DocumentReceivedFlag)
BEGIN
IF (SELECT DocumentReceivedFlag FROM Inserted) = 'Y' AND (SELECT DocumentReceivedFlag FROM Deleted) = 'N'
BEGIN
SELECT #TotalDocs = count(*)
FROM DocumentRequestContents
INNER JOIN Inserted
ON DocumentRequestContents.DocumentRequestID = Inserted.DocumentRequestID
WHERE (DocumentRequestContents.DocumentIgnoreFlag <> 'Y' OR DocumentRequestContents.DocumentIgnoreFlag IS NULL)
SELECT #ReceivedDocs = count(*)
FROM DocumentRequestContents
INNER JOIN Inserted
ON DocumentRequestContents.DocumentRequestID = Inserted.DocumentRequestID
WHERE DocumentRequestContents.DocumentReceivedFlag = 'Y'
IF (#ReceivedDocs = #TotalDocs)
BEGIN
UPDATE DocumentRequest
SET RequestStatusTypCd = 'CMPLT',
CompletedDtm = getdate(),
CompletedUserID = 'SYSTEM',
ModifiedDtm = getdate(),
ModifiedUserID = 'SYSTEM'
FROM DocumentRequest
INNER JOIN Inserted
ON DocumentRequest.DocumentRequestId = Inserted.DocumentRequestId
END
END
END
END
Thanks,
Jason
The offending line appears to be here:
IF (SELECT DocumentReceivedFlag FROM Inserted) = 'Y' AND
(SELECT DocumentReceivedFlag FROM Deleted) = 'N' ...
This is essentially a "subquery". The IF statement is written with the expectation that there will only be one row in both inserted and deleted. Without knowing the details of what the business logic is here, I can only speculate, but most likely you'll be OK if you just rewrite that as:
IF EXISTS(SELECT 1 FROM inserted WHERE DocumentReceivedFlag = 'Y') AND
EXISTS(SELECT 1 FROM deleted WHERE DocumentReceivedFlag = 'N'))
But it might be more complicated than that... it could be that you actually need to join the inserted to deleted rows and check if the flag has actually changed from 'Y' to 'N'.
Check if you don't have a trigger on DocumentRequestContents as well.
On re-reading, the obvious problem is this part of the second trigger:
IF (SELECT DocumentReceivedFlag FROM Inserted) = 'Y'
AND (SELECT DocumentReceivedFlag FROM Deleted) = 'N'
This would fail if multiple rows where inserted, because SQL Server expects both of these subqueries to return one or no rows.
By the way, I personally avoid triggers at any cost. They introduce too much complexity for me to handle.
You may want to try aliasing the Inserted table in your join. While I haven't experienced this myself, it may be attempting to perform a subquery in your WHERE clause rather than using the joined table.
Your trigger doesn't appear to have a sub-query anywhere in it - this is the only trigger on your table? This error isn't caused by a join or a multi-row update, so I'm at a loss as to why your trigger would be generating this error.
Do you get the error when you're attempting the insert into the table itself? Perhaps it's actually the query you're using to insert that's failing - please post that insert statement as well.