MSSQL - Enum

26. May 2011 18:38

 

Some database engines support enum. Some database engines do not. Or do they? If you have ever tried to create a table with a field with an enum you will know that it is not possbile. However it is possible to simulate the same functionality using a constraint.

 

Here is a quick chunk of code to demonstrate how this is possible. What we are aiming for is to make sure that on a contact table a gender field is either populated with M, F, U (male / female / unknown).

 

Lets create a customer table with the following.

 

CREATE TABLE [dbo].[Customer](
	[FirstName] [nvarchar](64) NULL,
	[LastName] [nvarchar](64) NULL,
	[Gender] [varchar](1) NULL,
) ON [PRIMARY]
GO

 

Now that we have a table we can change it slightly so our constraint will work.

 

ALTER TABLE [dbo].[Customer] 
	WITH CHECK ADD  CONSTRAINT [CK_CustomerGender]
		CHECK  (([Gender]='U' OR [Gender]='F' OR [Gender]='M'))
GO

 

Now we have the constraint we will be able to test it using the following. First of all lets insert a row which we know is valid

 

INSERT INTO Customer (FirstName, LastName, Gender)
	VALUES('fjskafs', 'fsvxxvbc', 'M')

 

Now lets try an invalid value

 

INSERT INTO Customer (FirstName, LastName, Gender)
	VALUES('fjskafs', 'fsvxxvbc', 'A')

 

We get the following error message

 

The INSERT statement conflicted with the CHECK constraint "CK_Customer". The conflict occurred in database "Mess", table "dbo.Customer", column 'Gender'

 

Now then table has the same functionality as an enum.

E-mail Kick it! DZone it! del.icio.us Permalink


MSSQL - RandomString Function

26. May 2011 00:05

 

If you have ever tried to create a random string function it can be a little tricky. It is very easy to do this in sql or in a stored procedure however it is not possible to call the function RAND() from inside a function on mssql server. An example of this is below ..

 

CREATE FUNCTION TestRand()
RETURNS int
BEGIN
	DECLARE @ret int
	SELECT @ret = CONVERT(int, RAND() * 100)
	RETURN @ret
END

 

If you attempt to run the code above you are just going to get the following error.

Invalid use of a side-effecting operator 'rand' within a function.

 

There is actually a very easy way to work around this problem by simply moving the RAND function outside of the function. A really easy way todo this is to create a view for it. Like this

 

CREATE VIEW [dbo].[Random]
	AS SELECT RAND() AS RAND
GO

 

So by doing this we can avoid the error above.

 

CREATE FUNCTION TestRand()
RETURNS int
BEGIN
	DECLARE @ret int
	SELECT @ret = CONVERT(int, RAND*100) FROM Random
	RETURN @ret
END
GO

SELECT dbo.TestRand()

 

So now that we have an easy way to generate random data inside a function. We can work with this to generate random strings of data. Using the following function.

 

CREATE FUNCTION RandomString (@str AS varchar(MAX), @len AS int)
RETURNS varchar(MAX)
BEGIN
	DECLARE @newstr VARCHAR(MAX)
	DECLARE @counter int

	SET @newstr = ''
	SET @counter = 0
	
	WHILE @counter < @len
	BEGIN
		
		SELECT @newstr = @newstr + SUBSTRING(@str, (SELECT CONVERT(int, (RAND * LEN(@str) + 1)) FROM Random), 1)
		SET @counter = @counter + 1
	END
		
	RETURN @newstr
END
GO

 

I am sure you are wondering what is the point in this. Well that is simple if you have ever attempted to generate specific test data in a stored procedure you end up doing a few loops a lot of function calls and repeating a lot of code. Now that the functions above are working using a tally table we can generate as much data as we will ever need! Here is an example ..

 

INSERT INTO Customer (FirstName, LastName, EMail)
	SELECT TOP 1000
		dbo.RandomString('abcdefghijklmnopqrstuvwxyz', 25) AS FirstName,
		dbo.RandomString('abcdefghijklmnopqrstuvwxyz', 25) AS LastName,
		dbo.RandomString('abcdefghijklmnopqrstuvwxyz', 6) + '@' + dbo.RandomString('abcdefghijklmnopqrstuvwxyz', 5) + '.local' AS EMail
			FROM Tally

Its simple, short, readable and easy to ajust to various database layouts.

E-mail Kick it! DZone it! del.icio.us Permalink


MSSQL - Log Sizes

25. May 2011 17:57

 

After almost running out of space on a server. I decided to reclaim some space from some of the larger transaction log files from some of the active databases. The problem with log files is they can always grow but they never really shrink unless you take action to actually make the files smaller again. So here is a stored procedure to list all the databases and their log sizes sorted with the largest first of course.

 

However this can be a pointless exercise if done incorrectly. It will only work if you have been doing some sort of work on a database (eg a migration) and the log files have grown much larger than their normal working size. So example of this would be to delete and import a new set of data. If you delete the data all in a single delete statements for a 10GB table then you will most likely have a log file of around 10GB or more. This will happen regardless even if you have the recovery model set to simple. After all during the transaction if you break a foreign key constrint or something goes wrong the database has to be able to roll back the transaction back to where it started before the delete statements. It will store this information in the log.

 

Anyway onto the code ...

 

 

CREATE PROCEDURE LogSize
AS
	SELECT DB_NAME(database_id) AS DBName,
            Name AS LogicalName,
            Physical_Name, (size * 8) / 1024 SizeMB
      FROM sys.master_files
            WHERE type_Desc = 'LOG'
      ORDER BY SizeMB DESC
GO

 

 

As a quick explenation of the above. We read the sys.master_files which contains a list of all the files the database engine is using. We then narrow the search to only log files which is one per database. We then take the size (in pages) and multiply it by 8 Kb. Then divide the answer again by 1024 to get it to a little more human friendly format in MB. After all we are not interested in trying to save a tiny amount of space.

 

Once we run the stored procedure using ..

 

EXEC LogSize

 

 

We will have a table of results

 

DBName LogicalName Physical_Name SizeMB

NetFlow NetFlow_log G:\MSSQL\Logs\NetFlow_log.ldf 513

 

We can then shrink the log files using the logical name and the dbcc command.

 

DBCC SHRINKFILE(NetFlow_log)

 

Or you could use another ShrinkLog I wrote a while ago. 

E-mail Kick it! DZone it! del.icio.us Permalink


MSSQL Removing the aspnet membership database

22. May 2011 22:01

If you have ever attempted to remove the aspnet_* tables, views and stored procedures from a database you will know that it is not simply stright forward. It takes a little bit of time but before you go disabling any constrints or various things it is possible to remove it from a database without too much effort. It is possible by just reordering the tables before they are deleted. The following will get the job done.

 

Before running this I would strongly suggest that you take some sort of backup of the database.

 

 

DROP VIEW vw_aspnet_Applications
DROP VIEW vw_aspnet_MembershipUsers
DROP VIEW vw_aspnet_Profiles
DROP VIEW vw_aspnet_Roles
DROP VIEW vw_aspnet_Users
DROP VIEW vw_aspnet_UsersInRoles
DROP VIEW vw_aspnet_WebPartState_Paths
DROP VIEW vw_aspnet_WebPartState_Shared
DROP VIEW vw_aspnet_WebPartState_User

DROP PROCEDURE aspnet_AnyDataInTables
DROP PROCEDURE aspnet_Applications_CreateApplication
DROP PROCEDURE aspnet_CheckSchemaVersion
DROP PROCEDURE aspnet_Membership_ChangePasswordQuestionAndAnswer
DROP PROCEDURE aspnet_Membership_CreateUser
DROP PROCEDURE aspnet_Membership_FindUsersByEmail
DROP PROCEDURE aspnet_Membership_FindUsersByName
DROP PROCEDURE aspnet_Membership_GetAllUsers
DROP PROCEDURE aspnet_Membership_GetNumberOfUsersOnline
DROP PROCEDURE aspnet_Membership_GetPassword
DROP PROCEDURE aspnet_Membership_GetPasswordWithFormat
DROP PROCEDURE aspnet_Membership_GetUserByEmail
DROP PROCEDURE aspnet_Membership_GetUserByName
DROP PROCEDURE aspnet_Membership_GetUserByUserId
DROP PROCEDURE aspnet_Membership_ResetPassword
DROP PROCEDURE aspnet_Membership_SetPassword
DROP PROCEDURE aspnet_Membership_UnlockUser
DROP PROCEDURE aspnet_Membership_UpdateUser
DROP PROCEDURE aspnet_Membership_UpdateUserInfo
DROP PROCEDURE aspnet_Paths_CreatePath
DROP PROCEDURE aspnet_Personalization_GetApplicationId
DROP PROCEDURE aspnet_PersonalizationAdministration_DeleteAllState
DROP PROCEDURE aspnet_PersonalizationAdministration_FindState
DROP PROCEDURE aspnet_PersonalizationAdministration_GetCountOfState
DROP PROCEDURE aspnet_PersonalizationAdministration_ResetSharedState
DROP PROCEDURE aspnet_PersonalizationAdministration_ResetUserState
DROP PROCEDURE aspnet_PersonalizationAllUsers_GetPageSettings
DROP PROCEDURE aspnet_PersonalizationAllUsers_ResetPageSettings
DROP PROCEDURE aspnet_PersonalizationAllUsers_SetPageSettings
DROP PROCEDURE aspnet_PersonalizationPerUser_GetPageSettings
DROP PROCEDURE aspnet_PersonalizationPerUser_ResetPageSettings
DROP PROCEDURE aspnet_PersonalizationPerUser_SetPageSettings
DROP PROCEDURE aspnet_Profile_DeleteInactiveProfiles
DROP PROCEDURE aspnet_Profile_DeleteProfiles
DROP PROCEDURE aspnet_Profile_GetNumberOfInactiveProfiles
DROP PROCEDURE aspnet_Profile_GetProfiles
DROP PROCEDURE aspnet_Profile_GetProperties
DROP PROCEDURE aspnet_Profile_SetProperties
DROP PROCEDURE aspnet_RegisterSchemaVersion
DROP PROCEDURE aspnet_Roles_CreateRole
DROP PROCEDURE aspnet_Roles_DeleteRole
DROP PROCEDURE aspnet_Roles_GetAllRoles
DROP PROCEDURE aspnet_Roles_RoleExists
DROP PROCEDURE aspnet_Setup_RemoveAllRoleMembers
DROP PROCEDURE aspnet_Setup_RestorePermissions
DROP PROCEDURE aspnet_UnRegisterSchemaVersion
DROP PROCEDURE aspnet_Users_CreateUser
DROP PROCEDURE aspnet_Users_DeleteUser
DROP PROCEDURE aspnet_UsersInRoles_AddUsersToRoles
DROP PROCEDURE aspnet_UsersInRoles_FindUsersInRole
DROP PROCEDURE aspnet_UsersInRoles_GetRolesForUser
DROP PROCEDURE aspnet_UsersInRoles_GetUsersInRoles
DROP PROCEDURE aspnet_UsersInRoles_IsUserInRole
DROP PROCEDURE aspnet_UsersInRoles_RemoveUsersFromRoles
DROP PROCEDURE aspnet_WebEvent_LogEvent


DROP TABLE aspnet_WebEvent_Events
DROP TABLE aspnet_SchemaVersions
DROP TABLE aspnet_Profile
DROP TABLE aspnet_UsersInRoles
DROP TABLE aspnet_Roles
DROP TABLE aspnet_PersonalizationPerUser
DROP TABLE aspnet_PersonalizationAllUsers
DROP TABLE aspnet_Paths
DROP TABLE aspnet_Membership
DROP TABLE aspnet_Users
DROP TABLE aspnet_Applications

DROP SCHEMA aspnet_Membership_FullAccess
DROP SCHEMA aspnet_Membership_ReportingAccess
DROP SCHEMA aspnet_Personalization_FullAccess
DROP SCHEMA aspnet_Membership_BasicAccess
DROP SCHEMA aspnet_Personalization_BasicAccess
DROP SCHEMA aspnet_Personalization_ReportingAccess
DROP SCHEMA aspnet_Profile_BasicAccess
DROP SCHEMA aspnet_Profile_FullAccess
DROP SCHEMA aspnet_Profile_ReportingAccess
DROP SCHEMA aspnet_Roles_BasicAccess
DROP SCHEMA aspnet_Roles_FullAccess
DROP SCHEMA aspnet_Roles_ReportingAccess
DROP SCHEMA aspnet_WebEvent_FullAccess


DROP ROLE aspnet_Membership_FullAccess
DROP ROLE aspnet_Membership_ReportingAccess
DROP ROLE aspnet_Personalization_FullAccess
DROP ROLE aspnet_Membership_BasicAccess
DROP ROLE aspnet_Personalization_BasicAccess
DROP ROLE aspnet_Personalization_ReportingAccess
DROP ROLE aspnet_Profile_FullAccess
DROP ROLE aspnet_Profile_ReportingAccess
DROP ROLE aspnet_Roles_FullAccess
DROP ROLE aspnet_Roles_BasicAccess
DROP ROLE aspnet_Roles_ReportingAccess
DROP ROLE aspnet_WebEvent_FullAccess
DROP ROLE aspnet_Profile_BasicAccess
E-mail Kick it! DZone it! del.icio.us Permalink


ASP.NET Account lockout notify

19. May 2011 21:05

 

Something that people normally overlook when building a system is logging so here is a little snippet of code that will detect when an account has become locked out making it possible to notify either support staff or the end user that there current account has been locked out.

 

This following works by taking a reading of the user account before and after the login event. So we read the status of the account on the page postback. Then we test for it during the login error. We can then log this or send an email to the current user.

 

 

public partial class Login : AppPage
{
    private bool AccountWasLocked = false;

    protected void Page_Load(object sender, EventArgs e)
    {
        if (IsPostBack == true)
        {
            MembershipUser tmp = Membership.GetUser(Login2.UserName);
            if (tmp != null)
                AccountWasLocked = tmp.IsLockedOut;
        }
    }

    protected void Login2_LoginError(object sender, EventArgs e)
    {
        MembershipUser tmp = Membership.GetUser(Login2.UserName);

        if (tmp != null && AccountWasLocked == false && tmp.IsLockedOut == true)
        {
            string Msg = string.Format("User '{0}' Has Locked Out their Account from IP Address '{1}'", tmp.UserName, Request.UserHostAddress);
            DBLog.Log(DBConn, tmp, "Login.LoginError.AccountLocked", Msg);
            //Notify Admin / End User
        }

        DBLog.Log(DBConn, "Login.Failure", string.Format("Login failed for user '{0}' from {1}", Login2.UserName, Request.UserHostAddress));
    }

    protected void Login2_LoggedIn(object sender, EventArgs e)
    {
        DBLog.Log(DBConn, "Login", string.Format("User '{0}' has logged in from {1}", Login2.UserName, Request.UserHostAddress));
    }
}

 

 

In the above code you will also need to include the namespace System.Web.Security to be able to access the MemberShip functions in asp.net.

 

The class DBLog that I am using has two static functions which you will also need to implement depending on how you wish to store your log information.

 

public class DBLog
{
    public static void Log(DBConn Conn, MembershipUser User, string LogType, string Message);
    public static void Log(DBConn Conn, string LogType, string Message);
}

 

I will probably be making some posts in the near future about logging in asp.net

E-mail Kick it! DZone it! del.icio.us Permalink