Loop through table with multiple rows and send one email - sql

As a noob, I am really battling with this.
I have a table as follows
SELECT [AuditFieldID]
,[CompanyRuleID]
,[CompanyRule]
,[VipUserName]
,[EffectiveDate]
,[FieldName]
,[SourceCode]
,[Action]
,[AccountNoOldValue]
,[AccountNoNewValue]
,[AccountTypeOldValue]
,[AccountTypeNewValue]
,[BankOldValue]
,[BankNewValue]
,[BranchOldValue]
,[BranchNewValue]
,[AccountHolderOldValue]
,[AccountHolderNewValue]
FROM [SageStaging].[MASSMART].[AuditCondensed]
There are 5 rows in the table for each employee. The first 5 fields contain the same data, and then the fieldname field, contains a different record type.
So you will have
Auditfieldid = 111111
CompanyRuleID = 12
CompanyRule = Walmart
VipUsername = john.doe
EffectiveDate = date()
Fieldname = 'Account Holder Name'
SourceCode = 1234 - John Doe
Action = I
AccountNoOldValue = NULL
AccountNoNewValue = NULL
AccountTypeOldValue = NULL
AccountTypeNewValue = NULL
BankOldValue = NULL
BankNewValue = NULL
BranchOldValue = NULL
BranchNewValue = NULL
AccountHoldOldValue = ''
AcccountHolderNewValue = 'John Doe'
There are five field name types:
FieldName = 'Account Holder Name'
FieldName = 'Account Number'
FieldName = 'Account Type'
FieldName = 'Bank'
FieldName = 'Bank Branch'
If the FieldName is = 'Account Holder' the record will have values in AccountHoldOldValue and AccountHoldNewValue
If FieldName is = 'Account Number' the record will have values in AccountNoOldValue and AccountNoNewValue
and so forth. So all in all you have 5 records of different fieldname types and in the row the appropriate value field populated according to the fieldname type.
I need to send an email with these values consolidated. So one email creating the following:
SET #MailSubject = 'Banking Details Change Notification for Employee' + ' ' + #SOURCECODE
SET #MessageBody = '<html>
<head>
<meta content="text/html; charset=ISO-8859-1"
http-equiv="content-type">
<title></title>
</head>
<body>
<br>
The following bank details have been changed:
<br>
<br>
Date Changed: ' + #EFFECTIVEDATE + '<br>' +
' Company: ' + #COMPANYRULE + '<br>' +
' Username: ' + #VIPUSERNAME + '<br>' +
' Employee Details: ' + #SOURCECODE + '<br>' +
' Action: ' + #ACTION + '<br>' +
' Account Holder: ' + ' Old Value: ' + #ACCOUNTHOLDEROLDVALUE + ' New Value: ' + #ACCOUNTHOLDERNEWVALUE + '<br>' +
' Account Number: ' + ' Old Value: ' + #ACCOUNTNOOLDVALUE + ' New Value: ' + #ACCOUNTNONEWVALUE + '<br>' +
' Account Type: ' + ' Old Value: ' + #ACCOUNTTYPEOLDVALUE + ' New Value: ' + #ACCOUNTTYPENEWVALUE + '<br>' +
' Bank: ' + ' Old Value: ' + #BANKOLDVALUE + ' New Value: ' + #BANKNEWVALUE + '<br>' +
' Bank Branch: ' + ' Old Value: ' + #BRANCHOLDVALUE + ' New Value: ' + #BRANCHNEWVALUE + '<br>' +
'<br>
<br>
<b>
Please do not respond to this email. If you have any questions regarding this email, please
contact your payroll administrator <br>
<br>
<br>
</body>'
I cannot seem to be able to figure out how to send just one email with all the necessary fields consolidated.
I seem to get five emails with blanks in all the fields, just the first four fields of the record gets populated.

This query should give you one record for each email.
select
a.Auditfieldid,
a.CompanyRuleID,
a.CompanyRule,
a.VipUsername,
a.EffectiveDate,
a.AccountHolderOldValue,
a.AccountHolderNewValue,
b.AccountNoOldValue,
b.AccountNoNewValue,
c.AccountTypeOldValue,
c.AccountTypeNewValue,
d.BankOldValue,
d.BankNewValue,
e.BranchOldValue,
e.BranchNewValue
from
(
select
Auditfieldid,
CompanyRuleID,
CompanyRule,
VipUsername,
EffectiveDate,
AccountHolderOldValue,
AccountHolderNewValue
from
SageStaging.MASSMART.AuditCondensed
where
FieldName = 'Account Holder Name'
) a
join
(
select
Auditfieldid,
CompanyRuleID,
CompanyRule,
VipUsername,
EffectiveDate,
AccountNoOldValue,
AccountNoNewValue
from
SageStaging.MASSMART.AuditCondensed
where
FieldName = 'Account Number'
) b
on
a.Auditfieldid = b.Auditfieldid and
a.CompanyRuleID = b.CompanyRuleID and
a.CompanyRule = b.CompanyRule and
a.VipUsername = b.VipUsername and
a.EffectiveDate = b.EffectiveDate
join
(
select
Auditfieldid,
CompanyRuleID,
CompanyRule,
VipUsername,
EffectiveDate,
AccountTypeOldValue,
AccountTypeNewValue
from
SageStaging.MASSMART.AuditCondensed
where
FieldName = 'Account Type'
) c
on
a.Auditfieldid = c.Auditfieldid and
a.CompanyRuleID = c.CompanyRuleID and
a.CompanyRule = c.CompanyRule and
a.VipUsername = c.VipUsername and
a.EffectiveDate = c.EffectiveDate
join
(
select
Auditfieldid,
CompanyRuleID,
CompanyRule,
VipUsername,
EffectiveDate,
BankOldValue,
BankNewValue
from
SageStaging.MASSMART.AuditCondensed
where
FieldName = 'Bank'
) d
on
a.Auditfieldid = d.Auditfieldid and
a.CompanyRuleID = d.CompanyRuleID and
a.CompanyRule = d.CompanyRule and
a.VipUsername = d.VipUsername and
a.EffectiveDate = d.EffectiveDate
join
(
select
Auditfieldid,
CompanyRuleID,
CompanyRule,
VipUsername,
EffectiveDate,
BranchOldValue,
BranchNewValue
from
SageStaging.MASSMART.AuditCondensed
where
FieldName = 'Bank Branch'
) e
on
a.Auditfieldid = e.Auditfieldid and
a.CompanyRuleID = e.CompanyRuleID and
a.CompanyRule = e.CompanyRule and
a.VipUsername = e.VipUsername and
a.EffectiveDate = e.EffectiveDate;
You have been given an appalling database design; I'm not surprised you find it difficult. The name suggests that it is condensed data. It isn't. It is actually a very wasteful structure.
I am concerned about the assertion that there are 5 rows for each employee. If some don't have all 5 rows you will have to do outer joins. You will need one reliable record type for each employee if this is the case.

From personal experience, whenever you build a string like that, concatenating a bunch of strings together, you should wrap each variable in an ISNULL check. One null variable, and the entire string will be null.
You should probably isolate the logic for creating the email body in a function. Here's a full example with sample DDL and DML statements for testing:
CREATE TABLE AuditCondensed (
[AuditFieldID] INT
,[CompanyRuleID] INT
,[CompanyRule] VARCHAR(10)
,[VipUserName] VARCHAR(10)
,[EffectiveDate] DATE
,[FieldName] VARCHAR(25)
,[SourceCode] VARCHAR(25)
,[Action] CHAR(1)
,[AccountNoOldValue] VARCHAR(25)
,[AccountNoNewValue] VARCHAR(25)
,[AccountTypeOldValue] VARCHAR(25)
,[AccountTypeNewValue] VARCHAR(25)
,[BankOldValue] VARCHAR(25)
,[BankNewValue] VARCHAR(25)
,[BranchOldValue] VARCHAR(25)
,[BranchNewValue] VARCHAR(25)
,[AccountHolderOldValue] VARCHAR(25)
,[AccountHolderNewValue] VARCHAR(25))
INSERT INTO AuditCondensed VALUES (111111, 12, 'Walmart', 'john.doe', GETDATE(), 'Account Number', '1234 - John Doe', 'I', '12345', '1234567-123', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)
INSERT INTO AuditCondensed VALUES (111111, 12, 'Walmart', 'john.doe', GETDATE(), 'Account Type', '1234 - John Doe', 'I', NULL, NULL, NULL, 'Savings', NULL, NULL, NULL, NULL, NULL, NULL)
INSERT INTO AuditCondensed VALUES (111111, 12, 'Walmart', 'john.doe', GETDATE(), 'Bank', '1234 - John Doe', 'I', NULL, NULL, NULL, NULL, 'Old Bank', 'New Bank', NULL, NULL, NULL, NULL)
INSERT INTO AuditCondensed VALUES (111111, 12, 'Walmart', 'john.doe', GETDATE(), 'Branch', '1234 - John Doe', 'I', NULL, NULL, NULL, NULL, NULL, NULL, 'Branch 1', 'Branch 2', NULL, NULL)
INSERT INTO AuditCondensed VALUES (111111, 12, 'Walmart', 'john.doe', GETDATE(), 'Account Holder', '1234 - John Doe', 'I', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, '', 'John Doe')
CREATE FUNCTION dbo.fn_create_email_body(#SourceCode VARCHAR(25)) RETURNS NVARCHAR(MAX)
AS
BEGIN
DECLARE #MailSubject NVARCHAR(200) = ''
DECLARE #MessageBody NVARCHAR(MAX) = ''
DECLARE #EFFECTIVEDATE DATE
DECLARE #COMPANYRULE VARCHAR(10)
DECLARE #VIPUSERNAME VARCHAR(10)
DECLARE #ACTION CHAR(1)
DECLARE #ACCOUNTHOLDEROLDVALUE VARCHAR(25)
DECLARE #ACCOUNTHOLDERNEWVALUE VARCHAR(25)
DECLARE #ACCOUNTNOOLDVALUE VARCHAR(25)
DECLARE #ACCOUNTNONEWVALUE VARCHAR(25)
DECLARE #ACCOUNTTYPEOLDVALUE VARCHAR(25)
DECLARE #ACCOUNTTYPENEWVALUE VARCHAR(25)
DECLARE #BANKOLDVALUE VARCHAR(25)
DECLARE #BANKNEWVALUE VARCHAR(25)
DECLARE #BRANCHOLDVALUE VARCHAR(25)
DECLARE #BRANCHNEWVALUE VARCHAR(25)
SELECT TOP 1 #EFFECTIVEDATE = EffectiveDate, #COMPANYRULE = CompanyRule, #VIPUSERNAME = VipUserName, #ACTION = Action,
#ACCOUNTHOLDEROLDVALUE = (SELECT AccountHolderOldValue FROM AuditCondensed WHERE SourceCode = #SourceCode AND FieldName = 'Account Holder'),
#ACCOUNTHOLDERNEWVALUE = (SELECT AccountHolderNewValue FROM AuditCondensed WHERE SourceCode = #SourceCode AND FieldName = 'Account Holder'),
#ACCOUNTNOOLDVALUE = (SELECT AccountNoOldValue FROM AuditCondensed WHERE SourceCode = #SourceCode AND FieldName = 'Account Number'),
#ACCOUNTNONEWVALUE = (SELECT AccountNoNewValue FROM AuditCondensed WHERE SourceCode = #SourceCode AND FieldName = 'Account Number'),
#ACCOUNTTYPEOLDVALUE = (SELECT AccountTypeOldValue FROM AuditCondensed WHERE SourceCode = #SourceCode AND FieldName = 'Account Type'),
#ACCOUNTTYPENEWVALUE = (SELECT AccountTypeNewValue FROM AuditCondensed WHERE SourceCode = #SourceCode AND FieldName = 'Account Type'),
#BANKOLDVALUE = (SELECT BankOldValue FROM AuditCondensed WHERE SourceCode = #SourceCode AND FieldName = 'Bank'),
#BANKNEWVALUE = (SELECT BankNewValue FROM AuditCondensed WHERE SourceCode = #SourceCode AND FieldName = 'Bank'),
#BRANCHOLDVALUE = (SELECT BranchOldValue FROM AuditCondensed WHERE SourceCode = #SourceCode AND FieldName = 'Branch'),
#BRANCHNEWVALUE = (SELECT BranchNewValue FROM AuditCondensed WHERE SourceCode = #SourceCode AND FieldName = 'Branch')
FROM AuditCondensed
WHERE SourceCode = #SourceCode
SET #MessageBody = '<html>
<head>
<meta content="text/html; charset=ISO-8859-1"
http-equiv="content-type">
<title></title>
</head>
<body>
<br>
The following bank details have been changed:
<br>
<br>
Date Changed: ' + ISNULL(FORMAT(#EFFECTIVEDATE, 'M/d/yyyy'), '') + '<br>' +
' Company: ' + ISNULL(#COMPANYRULE, '') + '<br>' +
' Username: ' + ISNULL(#VIPUSERNAME, '') + '<br>' +
' Employee Details: ' + ISNULL(#SOURCECODE, '') + '<br>' +
' Action: ' + ISNULL(#ACTION, '') + '<br>' +
' Account Holder: ' + ' Old Value: ' + ISNULL(#ACCOUNTHOLDEROLDVALUE, '') + ' New Value: ' + ISNULL(#ACCOUNTHOLDERNEWVALUE, '') + '<br>' +
' Account Number: ' + ' Old Value: ' + ISNULL(#ACCOUNTNOOLDVALUE, '') + ' New Value: ' + ISNULL(#ACCOUNTNONEWVALUE, '') + '<br>' +
' Account Type: ' + ' Old Value: ' + ISNULL(#ACCOUNTTYPEOLDVALUE, '') + ' New Value: ' + ISNULL(#ACCOUNTTYPENEWVALUE, '') + '<br>' +
' Bank: ' + ' Old Value: ' + ISNULL(#BANKOLDVALUE, '') + ' New Value: ' + ISNULL(#BANKNEWVALUE, '') + '<br>' +
' Bank Branch: ' + ' Old Value: ' + ISNULL(#BRANCHOLDVALUE, '') + ' New Value: ' + ISNULL(#BRANCHNEWVALUE, '') + '<br>' +
'<br>
<br>
<b>
Please do not respond to this email. If you have any questions regarding this email, please
contact your payroll administrator <br>
<br>
<br>
</body>'
RETURN #MessageBody
END
GO
SELECT dbo.fn_create_email_body('1234 - John Doe')
Note the query in the function that sets all the variable values. It uses TOP to set the values for all the constant variables, and subqueries to conditionally set the variables that are dependent on the FieldName. If you use this, you'll need to make sure the data types of the variables (and lengths) match your database.

Perhaps another approach dbFiddle
Example
-- Just creating a DUMMY dataset --
-----------------------------------
Declare #YourTable table (ID int,Active bit,First_Name varchar(50),Last_Name varchar(50),EMail varchar(50))
Insert into #YourTable values
(1,1,'John','Smith','johnsmith.#email.com'),
(2,0,'Jane','Doe','janedoe.#email.com')
-- Here we have our Pattern/Template --
---------------------------------------
Declare #Pattern varchar(max) = '
Dear [[First_Name]] [[Last_Name]]
Please confirm your email address <b>[[EMail]]</b>
Thank You
'
-- The actual query --
----------------------
Select A.ID
,Formatted = [dbo].[svf-Str-Tokenize]((Select A.* for XML Raw),#Pattern)
From #YourTable A
Returns
ID Formated
1 Dear John Smith
Please confirm your email address <b>johnsmith.#email.com</b>
Thank You
2 Dear Jane Doe
Please confirm your email address <b>janedoe.#email.com</b>
Thank You
The UDF if Interested
CREATE FUNCTION [dbo].[svf-Str-Tokenize](#XML xml,#Template varchar(max))
Returns varchar(max)
Begin
Select #Template = Replace(#Template,Item,Value)
From (
Select Item = '[['+attr.value('local-name(.)','varchar(100)')+']]'
,Value = attr.value('.','varchar(max)')
From #XML.nodes('/row') as xn(n)
Cross Apply xn.n.nodes('./#*') AS xa(attr)
Union All
Select Item = left(Item,charindex(']]',Item)+1)
,Value = ' '
From (
Select Item = ltrim(rtrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(replace(#Template,'[[','||[['),'||','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
) P
Where charindex('[[',Item)>0 and charindex(']]',Item)>0
) A
Return ltrim(rtrim(replace(replace(replace(#Template,' ','><'),'<>',''),'><',' ')))
End

Related

How to compare data from multiple databases

I'm trying to compare some data from different multiple databases, as I have illustrate my current case, I have there databases, database 1 is the main, and time to time database 2 and database 3 are updated from database 1. I have some difficulties to get the final result which return the data from database 1 and two columns column show the availability in database 2 as Yes or No, and the same with second extra column that will indicate the data availability on the database 3 with Yes or NO.
SELECT *
FROM (
Select ID as db1_ID,
First_name as db1_First_name,
Last_name as db1_Last_name,
Email as db1_Email,
Password as db1_Password,
Request_Id as db1_Request_Id,
User_Id as db1_User_Id,
Request_name as db1_Request_name
from User
inner join User_request
on User_request.User_Id = user.ID
) AS DB1_VIEW
LEFT OUTER JOIN
(
Select ID as db2_ID,
First_name as db2_First_name,
Last_name as db2_Last_name,
Email as db2_Email,
Password as db2_Password,
Request_Id as db2_Request_Id,
User_Id as db2_User_Id,
Request_name as db2_Request_name
from User
inner join User_request
on User_request.User_Id = user.ID
) AS DB2_VIEW
ON db2_ID = db1_ID
LEFT OUTER JOIN
(
Select ID as db3_ID,
First_name as db3_First_name,
Last_name as db3_Last_name,
Email as db3_Email,
Password as db3_Password,
Request_Id as db3_Request_Id,
User_Id as db3_User_Id,
Request_name as db3_Request_name
from User
inner join User_request
on User_request.User_Id = user.ID
) AS DB3_VIEW
ON db3_ID = db1_ID
ID First_name Last_name Email Password Request_Id User_Id Request_name
1 Oliver Jake OJake#domain.com 123 1 1 Request1
2 Mathew Harry MHarry#domain.com 123 1 2 Request1
3 Jacob Reece JReece#domain.com 123 1 3
Request1
4 Charlie Damian CDamian#domain.com 123 1 4 Request1
Use this as your first select statement:
SELECT DB1_VIEW.*
,CASE WHEN DB2_VIEW.db2_ID IS NOT NULL THEN 'Y' ELSE 'N' END AS Available_db2
,CASE WHEN DB3_VIEW.db3_ID IS NOT NULL THEN 'Y' ELSE 'N' END AS Available_db3
You can remove all the details apart from the ID fields in the db2_view and db3_view subqueries.
You can use the below query before execute you should use replace [SourceDB] to your source database and [TargertDB] to your target database. Insert the table name into #mdtables to include for comparison.
USE [SourceDB]
IF Object_id('tempdb..#mdTables') IS NOT NULL
DROP TABLE #mdtables;
CREATE TABLE #mdtables
(
id INT IDENTITY(1, 1) NOT NULL,
schemaname NVARCHAR(128),
tablename NVARCHAR(128)
);
INSERT INTO #mdtables
(schemaname,
tablename)
VALUES ('dbo',
'user');
DECLARE #mdTableLim INT =0,
#mdTableRowId INT =0
SELECT #mdTableLim = Count(*)
FROM #mdtables;
SET #mdTableRowId = 1;
WHILE #mdTableRowId <= #mdTableLim
BEGIN
DECLARE #SDBName VARCHAR(50) = '[SourceDB]',
#TDBName VARCHAR(50) = '[TargertDB]',
#tableName VARCHAR(100) = ''
DECLARE #WhereF VARCHAR(max) ='',
#joincondition VARCHAR(max) ='',
#or VARCHAR(10) ='',
#select VARCHAR(max) = '',
#comma VARCHAR(1)='',
#query VARCHAR(max) ='',
#and VARCHAR(5)='',
#where1 VARCHAR(1000) ='',
#wOR VARCHAR(5)=''
SELECT #tableName = tablename
FROM #mdtables
WHERE id = #mdTableRowId;
SELECT #joincondition += Isnull(#and + ( CASE
WHEN cu.column_name IS NULL
THEN
NULL
ELSE ' src.[' + cu.column_name
+
'] = ' +
'trgt.['
+ c.column_name + ']'
END ), ''),
#WhereF += Isnull (#or + ( CASE
WHEN cu.column_name IS NOT NULL THEN
NULL
ELSE Isnull ( ' src.[' +
TC.column_name
+
'] ',
' isnull( src.[' +
C.column_name +
'],1) ' )
+ Isnull( '<> trgt.[' +
TC.column_name
+ ']',
' = isnull (src.['
+
C.column_name + '],1) ')
END ), ''),
#or = ( CASE
WHEN cu.column_name IS NOT NULL THEN ''
ELSE ' OR '
END ),
#and = ( CASE
WHEN cu.column_name IS NULL THEN ''
ELSE ' AND '
END ),
#select += #comma + ' src.[' + c.column_name + '] '
+ Isnull (' , trgt.[' + TC.column_name + ']', ''),
#comma = ',',
#where1 += Isnull(( #wOR + ( CASE
WHEN cu.column_name IS NULL THEN
NULL
ELSE ' trgt.[' + cu.column_name +
'] is null '
END ) ), ''),
#wOR = ( CASE
WHEN cu.column_name IS NULL THEN ''
ELSE ' OR '
END )
FROM information_schema.columns C
LEFT JOIN information_schema.key_column_usage CU
ON C.column_name = cu.column_name
AND constraint_name LIKE 'PK_%'
AND c.table_name = cu.table_name
LEFT JOIN [TargertDB].information_schema.columns TC
ON C.column_name = TC.column_name
AND c.table_name = TC.table_name
WHERE c.table_name = #tableName
--AND columnproperty(object_id(TABLE_NAME), COLUMN_NAME, 'IsIdentity') = 0
AND c.column_name NOT IN ( 'LST_CHG_TMS', 'LST_CHG_TMS',
'LST_CHG_USR_ID'
,
'LST_CHG_USR_ID' )
AND c.data_type NOT IN ( 'image' )
ORDER BY cu.column_name
SET #query = 'select ' + #select + ' from ' + #SDBName + '.dbo.'
+ #tableName + ' as src left join ' + #TDBName
+ '.dbo.' + #tableName + ' as trgt on '
+ #joincondition + ' where (' + #where1 + ')'
+ Isnull ('and '+ NULLIF (#WhereF, ''), '')
DECLARE #qu1 VARCHAR(max) =
' declare #cnt int =0 select #cnt =count (1) from '
+ #SDBName + '.dbo.' + #tableName
+ ' as src left join ' + #TDBName + '.dbo.'
+ #tableName + ' as trgt on ' + #joincondition
+ ' where (' + #where1 + ')'
+ Isnull (' OR '+ NULLIF (#WhereF, ''), '')
+ ' if (#cnt>0) begin select '''
+ #tableName + ''' as [ ],#cnt ' +-- #query + ' end '
BEGIN try
EXECUTE ( #qu1)
END try
BEGIN catch
PRINT #qu1;
END catch
SET #mdTableRowId = #mdTableRowId + 1
END
This might not need CTE's or sub-queries.
A few joins might do it.
SELECT
Usr1.ID AS db1_User_Id,
Usr1.First_name AS db1_First_name,
Usr1.Last_name AS db1_Last_name,
Usr1.Email AS db1_Email,
Usr1.Password AS db1_Password,
MAX(UsrReq1.Request_Id) AS db1_Request_Id,
MAX(UsrReq1.Request_name) AS db1_Request_name,
CASE WHEN COUNT(UsrReq2.User_Id) > 0 THEN 'Y' ELSE 'N' END AS Available_Db2,
CASE WHEN COUNT(UsrReq3.User_Id) > 0 THEN 'Y' ELSE 'N' END AS Available_Db3
FROM [Database1].[User] AS Usr1
LEFT JOIN [Database1].[User_request] AS UsrReq1 ON UsrReq1.User_Id = Usr1.ID
LEFT JOIN [Database2].[User] AS Usr2 ON Usr2.ID = Usr1.ID
LEFT JOIN [Database2].[User_request] AS UsrReq2 ON UsrReq2.User_Id = Usr2.ID
LEFT JOIN [Database3].[User] AS Usr3 ON Usr3.ID = Usr1.ID
LEFT JOIN [Database3].[User_request] AS UsrReq3 ON UsrReq3.User_Id = Usr3.ID
GROUP BY
Usr1.ID,
Usr1.First_name,
Usr1.Last_name,
Usr1.Email,
Usr1.Password;

Function Within a View SQL Server Management Studio

Hey I am having a problem with a View I am trying to create I am trying to get the Full Address of a member be Displayed in correlation with the MemberID of my Member Table along with a few other fields in other tables but the FullAddress and MFullAddress are both coming up as NULL Values and I am not sure why hope someone can help. Below is the Code for my View.
SELECT dbo.Member.MemberID, dbo.Member.Title + ' ' + dbo.Member.Forename + ' ' + dbo.Member.Surname AS Fullname, dbo.GetMemberFullAddress(dbo.Member.MemberID) AS FullAddress,
dbo.GetMemberFullMailingAddress(dbo.Member.MemberID) AS MFullAddress, dbo.Lookup_ActionType.Description, dbo.Payment.Amount
FROM dbo.Payment RIGHT OUTER JOIN
dbo.Member ON dbo.Payment.PaymentID = dbo.Member.MemberID RIGHT OUTER JOIN
dbo.Action LEFT OUTER JOIN
dbo.Lookup_ActionType ON dbo.Action.ActionTypeID = dbo.Lookup_ActionType.ActionTypeID ON dbo.Member.MemberID = dbo.Action.MemberID
WHERE (dbo.Member.MemberID = dbo.Member.MemberID)
And here are Both of my Functions Called dbo.GetFullMemberAddress The Mfulladdress function is the same only different fields
USE [ICOM.Database]
GO
/****** Object: UserDefinedFunction [dbo].[GetMemberFullAddress] Script Date: 22/10/2014 11:53:38 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <Richard Glass>
-- Create date: <22/10/2014>
-- Description: <Returns Full Address>
-- =============================================
ALTER FUNCTION [dbo].[GetMemberFullAddress]
-- Add the parameters for the function here
(#MemberID as integer)
RETURNS varchar(250)
AS
BEGIN
DECLARE #AddressLine as varchar(40)
DECLARE #FullAddress as varchar(250)
SET #FullAddress = (SELECT LTRIM(ISNULL(Title + ' ', '') + ForeName + ' ' + Surname) AS FullName FROM Member WHERE MemberID = #MemberID) + CHAR(10)
SET #FullAddress = #FullAddress + #AddressLine + CHAR(10)
SET #AddressLine = (SELECT Member.AddressLine1 FROM Member WHERE MemberID = #MemberID)
SET #FullAddress = #FullAddress + #AddressLine + CHAR(10)
SET #AddressLine = (SELECT AddressLine2 FROM Member WHERE MemberID = #MemberID)
IF #AddressLine <> ''
BEGIN
SET #FullAddress = #FullAddress + #AddressLine + CHAR(10)
END
SET #AddressLine = (SELECT AddressLine3 FROM Member WHERE MemberID = #MemberID)
IF #AddressLine <> ''
BEGIN
SET #FullAddress = #FullAddress + #AddressLine + CHAR(10)
END
SET #AddressLine = (SELECT Town FROM Member WHERE MemberID = #MemberID)
IF #AddressLine <> ''
BEGIN
SET #FullAddress = #FullAddress + #AddressLine + CHAR(10)
END
SET #AddressLine = (SELECT PostCode FROM Member WHERE MemberID = #MemberID)
SET #FullAddress = #FullAddress + #AddressLine
RETURN #FullAddress
END
AND
The problem is because concatenetating NULL with anything yields NULL by default. At the start of the function you have:
DECLARE #AddressLine as varchar(40)
DECLARE #FullAddress as varchar(250)
SET #FullAddress = (SELECT LTRIM(ISNULL(Title + ' ', '') + ForeName + ' ' + Surname) AS FullName FROM Member WHERE MemberID = #MemberID) + CHAR(10)
SET #FullAddress = #FullAddress + #AddressLine + CHAR(10)
And the problem line is:
SET #FullAddress = #FullAddress + #AddressLine + CHAR(10)
At this point #AddressLine is NULL, therefore you set #FullAddress to NULL.
You can get your full address though in a much less convoluted fashion, something like:
(LTRIM(ISNULL(Title + ' ', '') + ForeName + ' ' + Surname) + ',' +
CASE WHEN ISNULL(AddressLine1, '') = '' THEN '' ELSE AddressLine1 + ',' END +
CASE WHEN ISNULL(AddressLine2, '') = '' THEN '' ELSE AddressLine2 + ',' END +
CASE WHEN ISNULL(AddressLine3, '') = '' THEN '' ELSE AddressLine3 + ',' END +
CASE WHEN ISNULL(Town, '') = '' THEN '' ELSE Town + ',' END +
CASE WHEN ISNULL(PostCode, '') = '' THEN '' ELSE PostCode END
Although seemingly quite verbose, it does not require repeatedly querying the member table. You could then just add this as a computed column:
I would be inclined to add MemberAddress as a computed column to dbo.Member though, something like:
ALTER TABLE dbo.Member
ADD MemberAddress AS
(LTRIM(ISNULL(Title + ' ', '') + ForeName + ' ' + Surname) + ',' +
CASE WHEN ISNULL(AddressLine1, '') = '' THEN '' ELSE AddressLine1 + ',' END +
CASE WHEN ISNULL(AddressLine2, '') = '' THEN '' ELSE AddressLine2 + ',' END +
CASE WHEN ISNULL(AddressLine3, '') = '' THEN '' ELSE AddressLine3 + ',' END +
CASE WHEN ISNULL(Town, '') = '' THEN '' ELSE Town + ',' END +
CASE WHEN ISNULL(PostCode, '') = '' THEN '' ELSE PostCode END;
Would your stored proc be better if the code was just this?
DECLARE #FullAddress as varchar(250)
SELECT FullAddress = LTRIM(ISNULL(Title + ' ', '') + ForeName + ' ' + Surname)
+ CHAR(10)
+ ISNULL(Member.AddressLine1 + CHAR(10),'')
+ ISNULL(Member.AddressLine2 + CHAR(10),'')
+ ISNULL(Member.AddressLine3 + CHAR(10),'')
+ ISNULL(Member.Town + CHAR(10),'')
+ ISNULL(Member.PostCode + CHAR(10),'')
FROM Member
WHERE MemberID = #MemberID
RETURN #FullAddress

SQL Server: Error message on dynamic procecure

I don't have much experience with dynamic procedures so the following was only an attempt.
The only part that is actually dynamic here is the table name which I tried to create by combining the fix part "MOC_" and the variable part #level.
Can someone tell me what I have to change here to make this work ?
The error I get when executing this points to the IF NOT EXISTS part:
Conversion failed when converting the nvarchar value 'IF NOT EXISTS (
SELECT *
FROM MOC_Nav2
WHERE itemID = ' to data type int.
My procedure:
ALTER PROCEDURE [dbo].[MOC_UpdateNav]
#level nvarchar(20),
#itemID int,
#parentID int,
#itemName nvarchar(100),
#sortID int,
#logStatus nvarchar(20),
#lastUpdate nvarchar(50),
#modBy varchar(50)
AS
BEGIN
SET NOCOUNT ON;
BEGIN
DECLARE #sql nvarchar(max)
SET #sql = 'IF NOT EXISTS
(
SELECT *
FROM MOC_' + #level + '
WHERE itemID = ' + #itemID + '
)
INSERT INTO MOC_' + #level + '
(
parentID,
itemName,
sortID,
logStatus,
lastUpdate,
modDate,
modBy
)
SELECT ' + #parentID + ',
' + #itemName + ',
' + #sortID + ',
' + #logStatus + ',
' + #lastUpdate + ',
GETDATE(),
' + #modBy + '
ELSE
UPDATE MOC_' + #level + '
SET parentID = ' + #parentID + ',
itemName = ' + #itemName + ',
sortID = ' + #sortID + ',
logStatus = ' + #logStatus + ',
lastUpdate = ' + #lastUpdate + ',
modDate = GETDATE(),
modBy = ' + #modBy + '
WHERE itemID = ' + #itemID + ''
EXEC(#sql)
END
END
#itemID is an int. Because it is used in the expression, everything else must be converted to a number, including your SQL text.
Convert #itemID (and other numbers) to nvarchar before concatenating it into the query:
SET #sql = N'IF NOT EXISTS
(
SELECT *
FROM MOC_' + #level + '
WHERE itemID = ' + cast(#itemID as nvarchar(30)) + ...

SQL trigger only returning one record during a delete

Below is a trigger I've created for a table in a SQL database (We are running SQL Server 2008). We are going to be doing updates, inserts, and deletes in bulk so we created this trigger and a "storage" table (TransactionLog) to capture all the activity. So far, this trigger works perfectly for inserts and updates. All the records wind up in the "storage" table with all appropriate values.
However, the problem occurs when we try to delete anything more than one record. The only record this trigger captures and sends to the "storage" table is the last record deleted. All others get lost.
CREATE TRIGGER [dbo].[trg_Agent_ALL]
ON [dbo].[Agent]
FOR INSERT, UPDATE, DELETE
AS
BEGIN
--TransactionLog variables
DECLARE #tableName char(25) = 'Agent'
DECLARE #transDate datetime
DECLARE #lastChangeOperator char(6)
DECLARE #tableString char(255)
DECLARE #action char(1) = 'I'
DECLARE #userId char(25)
--Trigger table variables
DECLARE #sNumber char(10)
DECLARE #controlId char(3)
DECLARE #entityType char(1)
DECLARE #firstName nvarchar(50)
DECLARE #lastName nvarchar(50)
DECLARE #suffix nvarchar(10)
DECLARE #corpName nvarchar(100)
IF EXISTS (SELECT * FROM deleted)
BEGIN
SET #action =
CASE
WHEN EXISTS (SELECT * FROM inserted) THEN 'U'
ELSE 'D'
END
END
IF #action = 'D'
BEGIN
SELECT #sNumber = sNumber, #lastChangeOperator = LastChangeOperator, #transDate = LastChangeDate, #entityType = EntityType,
#firstName = FirstName, #lastName = LastName, #suffix = NameSuffix, #corpName = CorporateName, #controlId = ControlId
FROM deleted
IF #firstName IS NULL SET #firstName = 'NULL'
IF #lastName IS NULL SET #lastName = 'NULL'
IF #suffix IS NULL SET #suffix = 'NULL'
IF #corpName IS NULL SET #corpName = 'NULL'
IF #controlId IS NULL SET #controlId = 'NULL'
SET #tableString = 'sNum:' + #sNumber + ' ' + 'EntType:' + #entityType + ' ' + 'Fname:' + #firstName + ' ' + 'Lname:' + #lastname + ' ' + 'suf:' + #suffix +
' ' + 'crpName:' + #corpName + ' ' + 'crlId:' + #controlId
END
ELSE
BEGIN
SELECT #sNumber = SymetraNumber, #lastChangeOperator = LastChangeOperator, #transDate = LastChangeDate, #entityType = EntityType,
#firstName = FirstName, #lastName = LastName, #suffix = NameSuffix, #corpName = CorporateName, #controlId = ControlId
FROM inserted
IF #firstName IS NULL SET #firstName = 'NULL'
IF #lastName IS NULL SET #lastName = 'NULL'
IF #suffix IS NULL SET #suffix = 'NULL'
IF #corpName IS NULL SET #corpName = 'NULL'
IF #controlId IS NULL SET #controlId = 'NULL'
SET #tableString = 'sNum:' + #sNumber + ' ' + 'EntType:' + #entityType + ' ' + 'Fname:' + #firstName + ' ' + 'Lname:' + #lastname + ' ' + 'suf:' + #suffix +
' ' + 'crpName:' + #corpName + 'crlId:' + #controlId
END
INSERT INTO TransactionLog (TransactionDate, Operator, TableName, Action, TableString, UserId)
VALUES (#transDate, 'Op', #tableName, #action, #tableString, #lastChangeOperator)
END
Based on the comments below I've altered the SQL code in the delete section. The hard-coded values seem to be working, however the main reason I placed them in there just to show those are the values I need for those specific columns. I have variables set with these values in the code above (see the DECLARE statements). This, however, is giving me the following error message:
Conversion failed when converting the varchar value 'P' to data type int.
This error is coming off of the EntityType attribute in the inner SELECT statement. What confuses me is that this column has a data type set to char(1), and the TableString column (the destination of the concatenated values) has a data type of nvarchar(255). No clue where the "int" is coming from...
IF #action = 'D'
BEGIN
INSERT INTO TransactionLog (TransactionDate, Operator, TableName, Action, TableString, UserId)
SELECT LastChangeDate, 'Op', 'Agent', 'D',
(SELECT CAST(CAST(sNumber as nvarchar) + ' ' + EntityType + ' ' + ISNULL(FirstName, ' ') + ' ' + ISNULL(LastName, ' ') + ' ' + ISNULL(NameSuffix, ' ') + ' ' +
ISNULL(CorporateName, ' ' ) + ' ' + ISNULL(CAST(ControlId as nvarchar), ' ') AS nvarchar) as TableString
FROM deleted), LastChangeOperator
FROM deleted
END
ELSE
EDIT
By casting the sNumber and controlId fields to nvarchar I was able to move past my previous error message. Right now, however I am receiving a different error message posted below:
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
FINAL EDIT
Altering the subquery like so allowed me to return multiple deleted records into the Audit table as I was only requesting one record at a time.
IF #action = 'D'
BEGIN
INSERT INTO TransactionLog (TransactionDate, Operator, TableName, Action, TableString, UserId)
SELECT LastChangeDate, 'Op', 'Agent', 'D',
CAST('SymNum:' + CAST(SymetraNumber as nvarchar(30)) + ' entType:' + EntityType + ' Fname:' + ISNULL(FirstName, 'NULL') + ' Lname:' + ISNULL(LastName, 'NULL') +
' suff:' + ISNULL(NameSuffix, 'NULL') + ' corpName:' + ISNULL(CorporateName, 'NULL' ) + ' ctrlId:' + ISNULL(CAST(ControlId as nvarchar(30)), 'NULL') AS nvarchar(30)) as TableString
, LastChangeOperator
FROM deleted
END
ELSE
That select #sNumber = sNumber, .. syntax will overwrite the variables once for each row, so you end up only with the last row's values.
And then you also do an insert into ... values (...), which can only insert one row.
You should try to rewrite it in the form:
insert into TransactionLog ( ... )
select sNumber, ... from deleted
You can use ISNULL(lastname, 'NULL') instead of your if-statements.

How can I improve this Mailing Address SQL Server SELECT Statement?

How can I format a Mailing Address so that I always push all non-null rows to the top? That is, I want to convert an address from the structure below to a mailing address.
Here is the structure:
[Line1] [varchar](50) NULL,
[Line2] [varchar](50) NULL,
[Line3] [varchar](50) NULL,
[City] [varchar](50) NULL,
[State] [varchar] (2) NULL,
[PostalCode] [varchar](50) NULL,
Here is some sample data:
Line1=
Line2=123 Some Address
Line3=
City=Royal Oak
State=MI
ZIP=45673-2312
Here is what the result should look like (4 distinct or separate fields should be returned):
MailAddress1=123 Some Address
MailAddress2=ROYAL OAK MI 45673-2312
MailAddress3=
MailAddress4=
I am using SQL Server 2005.
Someone wrote this logic in our company and it just seemed to complex (Note: this is not the whole SELECT statement):
,CASE
WHEN eai.Line1 IS NULL OR eai.Line1 = '' THEN
CASE
WHEN eai.Line2 IS NULL OR eai.Line2 = '' THEN
CASE
WHEN eai.Line3 IS NULL OR eai.Line3 = '' THEN ISNULL(LTRIM(RTRIM(eai.City)),'') + ' ' + ISNULL(LTRIM(RTRIM(eai.RegionCode)),'') + ' ' + ISNULL(LTRIM(RTRIM(eai.PostalCode)),'')
ELSE eai.Line3
END
ELSE eai.Line2
END
ELSE eai.Line1
END
,CASE
WHEN eai.Line1 IS NULL OR eai.Line1 = '' THEN
CASE
WHEN eai.Line3 IS NULL OR eai.Line3 = '' THEN ISNULL(LTRIM(RTRIM(eai.City)),'') + ' ' + ISNULL(LTRIM(RTRIM(eai.RegionCode)),'') + ' ' + ISNULL(LTRIM(RTRIM(eai.PostalCode)),'')
ELSE eai.Line3
END
ELSE
CASE
WHEN eai.Line2 IS NULL OR eai.Line2 = '' THEN
CASE
WHEN eai.Line3 IS NULL OR eai.Line3 = '' THEN ISNULL(LTRIM(RTRIM(eai.City)),'') + ' ' + ISNULL(LTRIM(RTRIM(eai.RegionCode)),'') + ' ' + ISNULL(LTRIM(RTRIM(eai.PostalCode)),'')
ELSE eai.Line3
END
ELSE eai.Line2
END
END
,CASE
WHEN eai.Line1 IS NULL OR eai.Line1 = '' THEN
CASE
WHEN eai.Line2 IS NULL OR eai.Line2 = '' THEN NULL
ELSE
CASE
WHEN eai.Line3 IS NULL OR eai.Line3 = '' THEN NULL
ELSE ISNULL(LTRIM(RTRIM(eai.City)),'') + ' ' + ISNULL(LTRIM(RTRIM(eai.RegionCode)),'') + ' ' + ISNULL(LTRIM(RTRIM(eai.PostalCode)),'')
END
END
ELSE
CASE
WHEN eai.Line2 IS NULL OR eai.Line2 = '' THEN
CASE
WHEN eai.Line3 IS NULL OR eai.Line3 = '' THEN NULL
ELSE ISNULL(LTRIM(RTRIM(eai.City)),'') + ' ' + ISNULL(LTRIM(RTRIM(eai.RegionCode)),'') + ' ' + ISNULL(LTRIM(RTRIM(eai.PostalCode)),'')
END
ELSE
CASE
WHEN eai.Line3 IS NULL OR eai.Line3 = '' THEN ISNULL(LTRIM(RTRIM(eai.City)),'') + ' ' + ISNULL(LTRIM(RTRIM(eai.RegionCode)),'') + ' ' + ISNULL(LTRIM(RTRIM(eai.PostalCode)),'')
ELSE eai.Line3
END
END
END
,CASE WHEN eai.Line2 IS NOT NULL AND eai.Line2 <> '' AND eai.Line3 IS NOT NULL AND eai.Line3 <> '' THEN eai.City + ' ' + eai.RegionCode + ' ' + eai.PostalCode ELSE NULL END
The way to do this is with an UNPIVOT. Here is the solution:
With AddrTable as (
Select AddrFld, MailAddr From (
Select Cast(ISNULL([Line1], '') as Varchar(102)) as [A1],
Cast(ISNULL([Line2], '') as Varchar(102)) as [A2],
Cast(ISNULL([Line3], '') as Varchar(102)) as [A3],
Cast(ISNULL(LTRIM(RTRIM(City)),'') + ' ' + ISNULL(LTRIM(RTRIM(RegionCode)),'') + ' ' + ISNULL(LTRIM(RTRIM(PostalCode)),'') as Varchar(102)) as A4
From TableName Where UniqueID=#UniqueID) p
Unpivot (MailAddr For AddrFld in ([A1], [A2], [A3], [A4])) as unpvt)
Select Row_Number() over (Order by (Case Len(MailAddr) When 0 then 1 else 0 end), AddrFld) as RN,
MailAddr From AddrTable
Order By RN
Here's the output:
Address1
Westby WI 55555
-empty line-
-empty line-
Note that I had to use "Varchar(102)" as the field length (unpivot requires that all fields be the same) because your City/Region/Postal can have up to 102 chars in total. Also, note that "#UniqueID" is the identifier for the record whose address you need. This returns four and always four rows containing the data you need for your address.
UPDATE: If you need to return this as a set of four columns rather than four rows, then just plop it into a view and then query the view with a Pivot. I've included the view here for completeness as I had to change the above just a bit when creating the view so the uniqueID field was included and no sort was done (the sort is now done in the query):
Create View AddressRows AS
With AddrTable as (
Select UniqueID, AddrFld, MailAddr From (
Select UniqueID,
Cast(ISNULL([Line1], '') as Varchar(102)) as [A1],
Cast(ISNULL([Line2], '') as Varchar(102)) as [A2],
Cast(ISNULL([Line3], '') as Varchar(102)) as [A3],
Cast(ISNULL(LTRIM(RTRIM(City)),'') + ' ' + ISNULL(LTRIM(RTRIM(RegionCode)),'') + ' ' + ISNULL(LTRIM(RTRIM(PostalCode)),'') as Varchar(102)) as A4
From TableName Where UniqueID=#UniqueID) p
Unpivot (MailAddr For AddrFld in ([A1], [A2], [A3], [A4])) as unpvt)
Select UniqueID,
Row_Number() over (Order by (Case Len(MailAddr) When 0 then 1 else 0 end), AddrFld) as RN,
MailAddr From AddrTable
And then, when you want to pull your matching "row" out, Pivot it back using this SQL (notice that I am querying again using UniqueID):
Select [Addr1], [Addr2], [Addr3], [Addr4] From (
Select Top 4 'Addr' + Cast(Row_Number() over (Order by RN) as Varchar(12)) as AddrCol, -- "Top 4" needed so we can sneak the "Order By" in
MailAddr
From AddressRows Where UniqueID=#UniqueID
) p PIVOT (Max([MailAddr]) for AddrCol in ([Addr1], [Addr2], [Addr3], [Addr4])
) as pvt
This returns:
Addr1 Addr2 Addr3 Addr4
-------------- ------------------ ------------- ------------------
Address1 Westby WI 54667
Here's a three-minutes-invested solution:
DECLARE #address TABLE (
[Line1] [varchar](50) NULL,
[Line2] [varchar](50) NULL,
[Line3] [varchar](50) NULL,
[City] [varchar](50) NULL,
[State] [varchar] (2) NULL,
[PostalCode] [varchar](50) NULL
)
INSERT INTO #address (
[Line1],
[Line2],
[Line3],
[City],
[State],
[PostalCode]
)
VALUES (
NULL,
'123 Some Address',
NULL,
'Royal Oak',
'MI',
'45673-2312'
)
SELECT * FROM #address
SELECT
ISNULL(Line1 + CHAR(13), '')
+ ISNULL(Line2 + CHAR(13), '')
+ ISNULL(Line3 + CHAR(13), '')
+ ISNULL(City + ' ', '')
+ ISNULL([State] + ' ', '')
+ ISNULL(PostalCode, '')
FROM #address
Result:
123 Some Address
Royal Oak MI 45673-2312
Fiddle with the control characters until you get the result you need.
SELECT
LTRIM(RTRIM(LINE1)) + LTRIM(RTRIM(LINE2)) + LTRIM(RTRIM(LINE3)) AS MailAddress1,
LTRIM(RTRIM(CITY)) + ' ' + LTRIM(RTRIM(STATE)) + ' ' + LTRIM(RTRIM(POSTALCODE)) AS MailAddress2
FROM MyTable