How to add code to initialize sql database - sql

I use codefirst and I use Elmah.
I recreated every time the database, you must manually add the code from the file:
/*
ELMAH - Error Logging Modules and Handlers for ASP.NET
Copyright (c) 2004-9 Atif Aziz. All rights reserved.
Author(s):
Atif Aziz, http://www.raboof.com
Phil Haacked, http://haacked.com
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
-- ELMAH DDL script for Microsoft SQL Server 2000 or later.
-- $Id: SQLServer.sql 677 2009-09-29 18:02:39Z azizatif $
DECLARE #DBCompatibilityLevel INT
DECLARE #DBCompatibilityLevelMajor INT
DECLARE #DBCompatibilityLevelMinor INT
SELECT
#DBCompatibilityLevel = cmptlevel
FROM
master.dbo.sysdatabases
WHERE
name = DB_NAME()
IF #DBCompatibilityLevel <> 80
BEGIN
SELECT #DBCompatibilityLevelMajor = #DBCompatibilityLevel / 10,
#DBCompatibilityLevelMinor = #DBCompatibilityLevel % 10
PRINT N'
===========================================================================
WARNING!
---------------------------------------------------------------------------
This script is designed for Microsoft SQL Server 2000 (8.0) but your
database is set up for compatibility with version '
+ CAST(#DBCompatibilityLevelMajor AS NVARCHAR(80))
+ N'.'
+ CAST(#DBCompatibilityLevelMinor AS NVARCHAR(80))
+ N'. Although
the script should work with later versions of Microsoft SQL Server,
you can ensure compatibility by executing the following statement:
ALTER DATABASE ['
+ DB_NAME()
+ N']
SET COMPATIBILITY_LEVEL = 80
If you are hosting ELMAH in the same database as your application
database and do not wish to change the compatibility option then you
should create a separate database to host ELMAH where you can set the
compatibility level more freely.
If you continue with the current setup, please report any compatibility
issues you encounter over at:
http://code.google.com/p/elmah/issues/list
===========================================================================
'
END
GO
/* ------------------------------------------------------------------------
TABLES
------------------------------------------------------------------------ */
CREATE TABLE [dbo].[ELMAH_Error]
(
[ErrorId] UNIQUEIDENTIFIER NOT NULL,
[Application] NVARCHAR(60) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[Host] NVARCHAR(50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[Type] NVARCHAR(100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[Source] NVARCHAR(60) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[Message] NVARCHAR(500) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[User] NVARCHAR(50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[StatusCode] INT NOT NULL,
[TimeUtc] DATETIME NOT NULL,
[Sequence] INT IDENTITY (1, 1) NOT NULL,
[AllXml] NTEXT COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL
)
ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[ELMAH_Error] WITH NOCHECK ADD
CONSTRAINT [PK_ELMAH_Error] PRIMARY KEY NONCLUSTERED ([ErrorId]) ON [PRIMARY]
GO
ALTER TABLE [dbo].[ELMAH_Error] ADD
CONSTRAINT [DF_ELMAH_Error_ErrorId] DEFAULT (NEWID()) FOR [ErrorId]
GO
CREATE NONCLUSTERED INDEX [IX_ELMAH_Error_App_Time_Seq] ON [dbo].[ELMAH_Error]
(
[Application] ASC,
[TimeUtc] DESC,
[Sequence] DESC
)
ON [PRIMARY]
GO
/* ------------------------------------------------------------------------
STORED PROCEDURES
------------------------------------------------------------------------ */
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
CREATE PROCEDURE [dbo].[ELMAH_GetErrorXml]
(
#Application NVARCHAR(60),
#ErrorId UNIQUEIDENTIFIER
)
AS
SET NOCOUNT ON
SELECT
[AllXml]
FROM
[ELMAH_Error]
WHERE
[ErrorId] = #ErrorId
AND
[Application] = #Application
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
CREATE PROCEDURE [dbo].[ELMAH_GetErrorsXml]
(
#Application NVARCHAR(60),
#PageIndex INT = 0,
#PageSize INT = 15,
#TotalCount INT OUTPUT
)
AS
SET NOCOUNT ON
DECLARE #FirstTimeUTC DATETIME
DECLARE #FirstSequence INT
DECLARE #StartRow INT
DECLARE #StartRowIndex INT
SELECT
#TotalCount = COUNT(1)
FROM
[ELMAH_Error]
WHERE
[Application] = #Application
-- Get the ID of the first error for the requested page
SET #StartRowIndex = #PageIndex * #PageSize + 1
IF #StartRowIndex <= #TotalCount
BEGIN
SET ROWCOUNT #StartRowIndex
SELECT
#FirstTimeUTC = [TimeUtc],
#FirstSequence = [Sequence]
FROM
[ELMAH_Error]
WHERE
[Application] = #Application
ORDER BY
[TimeUtc] DESC,
[Sequence] DESC
END
ELSE
BEGIN
SET #PageSize = 0
END
-- Now set the row count to the requested page size and get
-- all records below it for the pertaining application.
SET ROWCOUNT #PageSize
SELECT
errorId = [ErrorId],
application = [Application],
host = [Host],
type = [Type],
source = [Source],
message = [Message],
[user] = [User],
statusCode = [StatusCode],
time = CONVERT(VARCHAR(50), [TimeUtc], 126) + 'Z'
FROM
[ELMAH_Error] error
WHERE
[Application] = #Application
AND
[TimeUtc] <= #FirstTimeUTC
AND
[Sequence] <= #FirstSequence
ORDER BY
[TimeUtc] DESC,
[Sequence] DESC
FOR
XML AUTO
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
CREATE PROCEDURE [dbo].[ELMAH_LogError]
(
#ErrorId UNIQUEIDENTIFIER,
#Application NVARCHAR(60),
#Host NVARCHAR(30),
#Type NVARCHAR(100),
#Source NVARCHAR(60),
#Message NVARCHAR(500),
#User NVARCHAR(50),
#AllXml NTEXT,
#StatusCode INT,
#TimeUtc DATETIME
)
AS
SET NOCOUNT ON
INSERT
INTO
[ELMAH_Error]
(
[ErrorId],
[Application],
[Host],
[Type],
[Source],
[Message],
[User],
[AllXml],
[StatusCode],
[TimeUtc]
)
VALUES
(
#ErrorId,
#Application,
#Host,
#Type,
#Source,
#Message,
#User,
#AllXml,
#StatusCode,
#TimeUtc
)
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
I execute this code automatically?
I tried to use db.Database.ExecuteSqlCommand but I get lots of errors of the form:
Incorrect syntax near 'GO'.
Incorrect syntax near the keyword 'ALTER'.
Incorrect syntax near the keyword 'ALTER'.
Incorrect syntax near 'GO'.
Incorrect syntax near the keyword 'SET'.
Incorrect syntax near 'GO'.
Incorrect syntax near 'GO'.
Must declare the scalar variable "#ErrorId".
Incorrect syntax near 'GO'.
Incorrect syntax near 'GO'.
Incorrect syntax near 'GO'.
Incorrect syntax near 'GO'.
Must declare the scalar variable "#TotalCount".
Must declare the scalar variable "#PageIndex".
Must declare the scalar variable "#TotalCount".
Must declare the scalar variable "#Application".
Must declare the scalar variable "#PageSize".
Must declare the scalar variable "#PageSize".
Must declare the scalar variable "#Application".
Incorrect syntax near 'GO'.
Incorrect syntax near 'GO'.
Incorrect syntax near 'GO'.
Incorrect syntax near 'GO'.
Must declare the scalar variable "#ErrorId".
Incorrect syntax near 'GO'.
Incorrect syntax near 'GO'.

Try this migration, bearing in mind that it expects all GO statements to be on a single line and that the file uses \r\n line endings. I installed Elmah using the elmah.sqlserver NuGet package, which drops the SqlServer.sql file in the appropriate location. You will need to change the resource name to match your project.
public partial class Elmah : DbMigration
{
public override void Up()
{
var sqlStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MyProject.App_Readme.Elmah.SqlServer.sql");
using(var sqlStreamReader = new StreamReader(sqlStream))
{
string sqlScript = sqlStreamReader.ReadToEnd();
ExecuteSqlScript(sqlScript);
}
}
void ExecuteSqlScript(string sqlScript)
{
string[] sql = sqlScript.Split(new[] {"\r\nGO\r\n"}, StringSplitOptions.RemoveEmptyEntries);
foreach (var sqlCommand in sql)
{
if (!string.IsNullOrWhiteSpace(sqlCommand))
Sql(sqlCommand);
}
}
public override void Down()
{
DropTable("ELMAH_Error");
Sql("DROP PROCEDURE ELMAH_GetErrorXml");
Sql("DROP PROCEDURE ELMAH_GetErrorsXml");
Sql("DROP PROCEDURE ELMAH_LogError");
}
}
I

just to formalize my comment to the OP as an answer;
I think that the best practice here would be that only your code-first db should be affected by any radical changes (i.e. recreation of the db on the model changing). All other tables unrelated to that should be in an ApplicationServices db or similar and NOT in the core db. That way you don't create a support nightmare everytime you need to update your model with a simple property or data type change. Plus, you do of course end up losing the entire history of your logging (and other) db tables every time you make a change.
So, in a word, I think you're tackling the wrong problem with a potentially inappropriate solution.
This is certainly how we do it in our shop and in previous projects that I've worked on.

This is a series of SQL commands separated by GO statements (which aren't strictly T-SQL commands)
You can use SQL Managment Studio to execute the script, or the command line tool SQLCMD.exe

I don't think you should run that many batches on ExecuteSqlCommand.
Why don't you simply execute it on SSMS?

You'll need to parse your script, split it to separate SQL commands and execute each one of them individually through SqlCommand. Unfortunately, I'm not aware of a way to automate this.

The script requires you to separate the batches (GO is the batch separator) and send each batch individually to SQL Server. I actually have a sample project dbutilsqlcmd that does just that, and also supports more of the sqlcmd extensions like :setvar and $(variable) replacement in the script, which can be quite useful at deployment.

There is now a nuget package that will initialize the Elmah table and procs for you: https://www.nuget.org/packages/Elmah.SqlServer.EFInitializer/. Add it to your web project. It will then use EF code first to create it for you. It adds an Initializer to your App_Start folder so you actually don't have to add any code. It will add a migration to your database to ensure it only gets added once.

Related

Operand type clash when compiling a native stored procedure in SQL Server 2019

Any idea why I can compile this stored procedure with the first Insert but not the second (or both)? The error message is:
Msg 206, Level 16, State 2, Procedure InsertExtPageWithXML, Line 21 [Batch Start Line 11]
Operand type clash: numeric is incompatible with uniqueidentifier
THis is the SQL code:
--======================================================
-- Create Natively Compiled Stored Procedure Template
--======================================================
USE [PortalMO]
GO
-- Drop stored procedure if it already exists
IF OBJECT_ID('InsertExtPageWithXML','P') IS NOT NULL
DROP PROCEDURE [dbo].[InsertExtPageWithXML]
GO
CREATE PROCEDURE [dbo].[InsertExtPageWithXML]
-- Add the parameters for the stored procedure here
-- (not inserted is the auto-generated UI [PK_Id], non-null, primary key..)
(#1_Topic_PK uniqueidentifier = NULL,
#1_Path nvarchar(500) = "fix.me",
#1_Title nvarchar(450) = "fix.me",
#1_URL nvarchar(max) = "fix.me",
#1_Priority tinyint = NULL,
#1_Type nvarchar(50) = NULL,
#2_XMLfragment nvarchar(max) = "fix.me")
-- (all of the types above are accurate to the schemas already in existence)
WITH NATIVE_COMPILATION, SCHEMABINDING
AS BEGIN ATOMIC WITH
(
TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english'
)
--Insert statements for the stored procedure here
INSERT INTO [dbo].[ExternalPage] (Topic_PK, Path, Title, URL, Priority, Type, LastUpdated)
VALUES (#1_Topic_PK, #1_Path, #1_Title, #1_URL, #1_Priority, #1_Type, GETDATE());
INSERT INTO [dbo].[XML] (Associated_PK, Type, XMLfragment, LastUpdated)
VALUES (SCOPE_IDENTITY(), N'ExternalPage', #2_XMLfragment, GETDATE());
END
GO

sp_replmonitorhelppublisher - "An INSERT EXEC statement cannot be nested."

Issue:
I'm getting this error on Microsoft SQL Server 2008:
Msg 8164, Level 16, State 1, Procedure sp_MSload_tmp_replication_status, Line 80
An INSERT EXEC statement cannot be nested.
Background:
I'm trying to programmatically monitor replication status in Microsoft SQL Server. I'm using INSERT EXEC statement on sp_replmonitorhelppublisher to get the status, questions that will follow are not restricted to this proc, but I'm mentioning it because the proc is built-in, so I cannot rewrite it to get the data "the way it should be done". My (simplified) code to get the data is:
declare #t table (
publisher nvarchar(max) null,
distribution_db nvarchar(max) null,
status nvarchar(max) null,
warning nvarchar(max) null,
publicationcount nvarchar(max) null,
returnstamp nvarchar(max) null
)
insert into #t exec sp_replmonitorhelppublisher 'MY_PUBLISHER'
Questions:
I understand the MS SQL Server restriction that is causing the error, I guess that there is some INSERT EXEC statement somewhere inside the built-in proc. What I don't understand is:
Why it sometimes works without the error (I've seen it a couple of times run successfully)?
Why a workaround of running the same EXEC statement (not as part of INSERT EXEC) before the actual INSERT EXEC works without errors? I.e. this code works OK:
declare #t table (
publisher nvarchar(max) null,
distribution_db nvarchar(max) null,
status nvarchar(max) null,
warning nvarchar(max) null,
publicationcount nvarchar(max) null,
returnstamp nvarchar(max) null
)
exec sp_replmonitorhelppublisher 'MY_PUBLISHER' -- extra call before main call
insert into #t exec sp_replmonitorhelppublisher 'MY_PUBLISHER'
Is this workaround guaranteed to run without the error? And why?
Or is there some form of caching involved, that happens to be working for me, but is not guaranteed to work on every call?
Is there any better way of programmatically monitoring replication?
I was working on the same issue - programatically monitoring replication status. I've found out that executing sp_replmonitor* stored procedures also updates the table dbo.MSReplication_monitordata. This table holds the current status information of replication.
So, my solution was to execute the sp_replmonitorhelppublisher and then read the values from the dbo.MSReplication_monitordata.
For example: Starting the publication and I waiting for it to finish (in a SQL Server Agent job) is done by this:
USE [DB]
DECLARE #StartDateTime datetime = GETDATE()
EXEC sp_startpublication_snapshot #publication='publ_DB'
USE [distribution]
DECLARE #Status int = 1
WHILE #status NOT IN (2, 6)
BEGIN
WAITFOR DELAY '00:00:05'
EXEC sp_replmonitorhelppublisher
SELECT #Status = status FROM dbo.MSReplication_monitordata WHERE publication='publ_DB' AND agent_type=1 AND time_stamp>#StartDateTime
END
Hope this helps!

Table-valued parameter error in SQL Server

I'm working on a reporting module for a company project. Although we use an ORM for our application, I've decided to write stored procedures for the reports in anticipation of migrating to SSRS.
These stored procedures require table-valued parameter input. As such, I've created my table type:
USE MyDatabase
GO
/****** Object: UserDefinedTableType [dbo].[IntList] Script Date: 5/8/2013 5:20:59 PM ******/
CREATE TYPE [dbo].[IntList] AS TABLE(
[Id] [int] NOT NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (IGNORE_DUP_KEY = OFF)
)
GO
I have the following SQL Server stored proc:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
USE MyDatabase
GO
-- =============================================
-- Author: <lunchmeat317>
-- Create date: <05/06/2013>
-- Description: <File Type Report>
-- =============================================
ALTER PROCEDURE Report_FileType
#filetype varchar(20) = null,
#User intList READONLY,
#Group intList READONLY
AS
BEGIN
SET NOCOUNT ON;
/*
lf = LibraryFile
lfu = LibraryFileAssignedUser
lfg = LibraryFileAssignedGroup
*/
SELECT Extension AS FileType, COUNT(1) AS TotalFiles
FROM LibraryFile lf
LEFT JOIN LibraryFileAssignedUser lfu
ON (SELECT COUNT(1) FROM #User) != 0
AND lfu.LibraryFileId = lf.Id
LEFT JOIN LibraryFileAssignedGroup lfg
ON (SELECT COUNT(1) FROM #Group) != 0
AND lfg.LibraryFileId = lf.Id
WHERE ((#filetype IS NULL) OR (Extension = #filetype))
AND (
((#User IS NULL) OR (lfu.UserId IN (SELECT * FROM #User)))
OR ((#Group IS NULL) OR (lfg.HubGroupId IN (SELECT * FROM #Group)))
)
GROUP BY Extension
END
GO
When I attempt to alter the stored procedure, I continually get the error message
Msg 137, Level 16, State 1, Procedure Report_FileType
Must declare the scalar variable "#User".
Msg 137, Level 16, State 1, Procedure Report_FileType
Must declare the scalar variable "#Group".
I can't figure out why this is happening. If I do use a scalar type (and update my code to match) it works. However, when I try to use a TVP, I can't compile the stored procedure.
For what it's worth, I've added the type, but I haven't set the permission on it yet. However, I don't expect that would cause a compilation error; it would only cause an error at runtime (which I've dealt with before).
Does anyone have any experience with this issue? Thanks!
Interesting, I haven't used this before, but it seems that you cannot test for #User / #Group IS NULL since it is a table.
Cf.
https://dba.stackexchange.com/questions/30754/how-do-i-check-for-a-null-or-empty-table-valued-parameter
http://social.msdn.microsoft.com/Forums/en-US/transactsql/thread/c59f6b82-7639-42c7-ad90-a4ec7315a3bd/

A file activation error occurred. The physical file name 'N#filename

Below t-sql code compiles fine. But when I run it like
exec [SP_ATTACH_NW] N'C:\myfolder' I get
Msg 5105, Level 16, State 2, Procedure SP_ATTACH_NW, Line 14
A file activation error occurred. The physical file name 'N#mdfFileName' may be incorrect. Diagnose and correct additional errors, and retry the operation.
USE master
GO
SET ANSI_NULLS ON
GO
CREATE PROCEDURE [dbo].[SP_ATTACH_NW] (
#DestFolder varchar(255)
)
AS
BEGIN
SET NOCOUNT ON;
Declare #mdfFileName varchar(255)
Declare #ldfFileName varchar(255)
set #mdfFileName = #DestFolder + '\northwnd.mdf'
set #ldfFileName = #DestFolder + '\northwnd.ldf'
CREATE DATABASE [Northwind] ON
( FILENAME = N#mdfFileName ),
( FILENAME = N#ldfFileName )
FOR ATTACH
END
Pls advise. thanks
You can't have variables in the filename arguments of CREATE DATABASE (MSDN doesn't show #vars in the syntax)
The code above is literally looking for the constant "N#mdfFileName" as a filename.
You'd need dynamic SQL to build a string in, say, #MyBuiltSQL then run EXEC(#MyBuiltSQL)
Note: The "N" prefix here would not make #mdfFileName nvarchar anyway

Declare global variables for a batch of execution statements - sql server 2005

i have an SQL statement wherein i am trying to update the table on the client's machine.
the sql statement is as follows:
BEGIN TRANSACTION
DECLARE #CreatedBy INT
SELECT #CreatedBy = [User_Id]
FROM Users
WHERE UserName = 'Administrator'
--////////////////////////////////////////////////////////////////////
--////////////////////////////////////////////////////////////////////
PRINT #CreatedBy --(Works fine here and shows me the output)
PRINT N'Rebuilding [dbo].[Some_Master]'
ALTER TABLE [dbo].[Some_Master]
ADD [CreatedBy] [BIGINT] NULL,
[Reason] [VARCHAR](200) NULL
GO
PRINT #CreatedBy --(does not work here and throws me an error)
PRINT N'Updating data in [Some_Master] table'
UPDATE Some_Master
SET CreatedBy = #CreatedBy
COMMIT TRANSACTION
but i am getting the following error:
Must declare the scalar variable "#CreatedBy".
Now i have observed if i write the Print statement above the alter command it works fine and shows me its value, but if i try to print the value after the Alter command it throws me the error i specified above.
I dont know why ?? please help!
Thank you
It's because of the GO, which signals the end of a batch of commands. So after the GO, it is a separate batch whereby the variable #CreatedBy is no longer in scope.
Try removing the GO statements.
I think you need to remove the GO statement.
Remove the "GO" statement.