Executing parameterized stored procedures using PetaPoco - sql

It's been two days I'm wrestling with PetaPoco to implement a search solution (evidently with some search parameters) which has custom paging in database. I couldn't figure out how to configure PetaPoco paging with ASP.NET DataPager (which is a whole different question). Anyway I want to use custom paging in database.
I have a stored proc called GetUsersPaged like below:
ALTER PROCEDURE [dbo].[GetUsersPaged]
#startRowIndex int,
#maximumRows int,
#name nvarchar(300) = NULL,
#email nvarchar(100) = NULL
AS
BEGIN
SELECT *
FROM
(
SELECT *,
ROW_NUMBER() OVER(ORDER BY Id) AS RowRank
FROM UserInfo
WHERE
(Nickname LIKE '%'+#name+'%'
OR FirstName LIKE '%'+#name+'%'
OR LastName LIKE '%'+#name+'%'
OR #name IS NULL)
AND
(Email = #email OR #email IS NULL)
) AS UsersPagedList
WHERE RowRank BETWEEN #startRowIndex AND (#startRowIndex + #maximumRows)
END
and GetUsersCount stored proc like below:
ALTER PROCEDURE [dbo].[GetUsersCount]
#name nvarchar(300) = NULL,
#email nvarchar(100) = NULL
AS
BEGIN
SELECT COUNT(*)
FROM UserInfo
WHERE
(Nickname LIKE '%'+#name+'%'
OR FirstName LIKE '%'+#name+'%'
OR LastName LIKE '%'+#name+'%'
OR #name IS NULL)
AND
(Email = #email OR #email IS NULL)
END
Now I have two methods for calling these stored procs like below:
[DataObjectMethod(DataObjectMethodType.Select, false)]
public List<DAL.UserInfo> GetPagedUserSearchResults(int startRowIndex, int pageSize, string name, string email)
{
DBService dbService = new DBService();
var db = dbService.GetDatabase();
var list = new List<DAL.UserInfo>();
if(name != string.Empty && email != string.Empty)
list = db.Fetch<DAL.UserInfo>(#"EXEC GetUsersPaged ##startRowIndex = #0, ##maximumRows = #1,
##name = #2, ##email = #3", startRowIndex, pageSize, name, email);
else if(name == string.Empty && email != string.Empty)
list = db.Fetch<DAL.UserInfo>(#"EXEC GetUsersPaged ##startRowIndex = #0, ##maximumRows = #1,
##email = #2", startRowIndex, pageSize, email);
else if(name != string.Empty && email == string.Empty)
list = db.Fetch<DAL.UserInfo>(#"EXEC GetUsersPaged ##startRowIndex = #0, ##maximumRows = #1,
##name = #2", startRowIndex, pageSize, name);
else if(name == string.Empty && email == string.Empty)
list = db.Fetch<DAL.UserInfo>(#"EXEC GetUsersPaged ##startRowIndex = #0, ##maximumRows = #1"
,startRowIndex, pageSize);
return list;
}
[DataObjectMethod(DataObjectMethodType.Select, false)]
public int GetPagedUserSearchResultsCount(string name, string email)
{
DBService dbService = new DBService();
var db = dbService.GetDatabase();
IEnumerable<DAL.UserInfo> count = null;
if (name != string.Empty && email != string.Empty)
count = db.Query<DAL.UserInfo>("EXEC GetUsersCount ##name = #0, ##email = #1", name, email);
else if (name == string.Empty && email != string.Empty)
count = db.Query<DAL.UserInfo>("EXEC GetUsersCount ##email = #0", email);
else if(name != string.Empty && email == string.Empty)
count = db.Query<DAL.UserInfo>("EXEC GetUsersCount ##name = #0", name);
else if (name == string.Empty && email == string.Empty)
count = db.Query<DAL.UserInfo>("EXEC GetUsersCount");
return count.Count<DAL.UserInfo>();
}
My question is: everything is fine when I'm executing and testing stored procs in db (inside SQL Management Studio). All results respect to paging parameters. But when I call the same stored procs using PetaPoco, it fetches the whole thing and paging is ignored.
For example when I test stored procs in SQL Management Studio with
#startRowIndex = 0
#maximumRows = 1
and I don't send anything for #name and #email, it returns just one row which is totally correct. But the same things with PetaPoco, it returns 3 rows in the list. Any idea what's the problem?

Have you tried setting EnableAutoSelect = false? When I left this on the default setting, the call returned all records.

Related

Conditionally add to a WHERE clause if a parameter is not null

I have a stored procedure which accepts 3 parameters, can anyone advise what's the most efficient way to conditionally add to the where clause if a parameter is not null?
currently I have:
where ([LoggedIn] >= #dateFrom and [LoggedIn] <= #dateTo)
I would like to add to this if the parameter #email is not null, so conditionally doing the following
AND [Email] = #email
Thanks in advance, I hope that makes sense
There's no reason why you cannot dynamically build an SQL that has parameters in, in your code and dynamically add values for the parameters as you extend the SQL text.. it looks something like:
var sql = "SELECT * FROM Table WHERE ([LoggedIn] >= #dateFrom and [LoggedIn] <= #dateTo)";
var cmd = new SqlCommand(sql, connection);
cmd.Parameters.Add("#dateFrom", SqlDbType.DateTime).Value = dateFrom;
cmd.Parameters.Add("#dateTo", SqlDbType.DateTime).Value = dateTo;
if(email != null){
cmd.CommandText += " AND [Email] = #email";
cmd.Parameters.Add("#email", SqlDbType.VarChar, SIZE_OF_DB_COLUMN_HERE).Value = email;
}
You should, of course, choose your SqlDbType to match what is actually in your DB. If it's a datetime2(7) then it's a bit more wordy;
cmd.Parameters.Add(new SqlParameter("#paramName", SqlDbType.DateTime2) { Scale = 7, Value = someVariable });
If you're doing your DB access with Dapper, just extend the SQL text and the parameters collection in a similar way
var sql = "SELECT * FROM Table WHERE ([LoggedIn] >= #dateFrom and [LoggedIn] <= #dateTo)";
var d = new Dictionary<string, object>(){
{ "#dateFrom", dateFrom },
[ "#dateTo", dateTo }
};
if(email != null){
sql += " AND [Email] = #email";
d["#email"] = email;
}
var r = conn.Query<TypeOfObjectHere>(sql, d);
If you're doing this with EF, you can leverage the fact that you can call Where multiple times and it works like AND:
var q = context.Logins.Where(l =~> l.LoggedIn >= dateFrom && l.LoggedIn <= dateTo);
if(email != null)
q = q.Where(l => l.Email == email);
var result = q.ToList();

SQL | Best practices to build SQL update query with not null values only

Looking for best practices to build an update query with not null values.
Below the my implementation where passed parameter firstName, middleName, lastName, address can be null. I want to build an update query with not null values only. I have used multiple if condition for not null and comma separator.
public void updatePerson(String id, String firstName, String middleName, String lastName, String address) {
StringBuilder query = new StringBuilder("UPDATE PERSON SET ");
MapSqlParameterSource mapSqlParameterSource = new MapSqlParameterSource();
mapSqlParameterSource.addValue("id", id);
boolean columnSepartor = false;
if (firstName != null) {
query.append(" FIRST_NAME =:firstName");
mapSqlParameterSource.addValue("firstName", firstName);
columnSepartor = true;
}
if (middleName != null) {
query = columnSepartor ? query.append(", MIDDLE_NAME =:middleName") : query.append(" MIDDLE_NAME =:middleName");
mapSqlParameterSource.addValue("middleName", middleName);
columnSepartor = true;
}
if (lastName != null) {
query = columnSepartor ? query.append(", LAST_NAME =:lastName") : query.append(" LAST_NAME =:lastName");
mapSqlParameterSource.addValue("lastName", lastName);
columnSepartor = true;
}
if (address != null) {
query = columnSepartor ? query.append(", ADDRESS =:address") : query.append(" ADDRESS =:address");
mapSqlParameterSource.addValue("address", address);
}
query.append(" WHERE ID =:id");
namedParameterJdbcTemplate.update(query.toString(), mapSqlParameterSource);
}
Please suggest best practices to target such use case.
Thanks in advance.
This would be my first choice of implementation for that requirement. It's small, it's easily understood and it will perform just as well for the vast majority of applications.
The coalesce expression returns the first non-null value. In this case it returns the new value (if there is one), else it returns the existing value from the column.
update person
set first_name = coalesce(:firstName, first_name)
,middle_name = coalesce(:middleName, middle_name)
,last_name = coalesce(:lastName, last_name)
,address = coalesce(:address, address)
where id = :id
You can build an SQL command as follows:
UPDATE <your table>
SET FirstName = NVL(PassedFirstName , FirstName) ,
MidName = NVL(PassedMidName , MidName ) ,
LastName = NVL(PassedLastName , LastName ) ...
WHERE...
which means, for any passed null value, update the record with the original value.

ASP.net Entity Framework Check if exists in database

I have VS2015, entity framework 6. I have a database with one table (Logins)
FirstName, lastName, Birthdate, email, password
I also have a textbox(s), button
tbEmail tbpass and btnLogin
How do I check if the users email in the textbox matches one in the database?
So far I have:
protected void btnLogin_Click(object sender, EventArgs e)
{
Logins Log = new Logins();
using (LoginDataEntities lg = new LoginDataEntities())
{
string #email = tbUsernameL.Text;
string #password = tbPassL.Text;
var logged = from L in lg.Logins
where L.Username == #email
&& L.Pass == #password
select L.Username;
if (logged != null)
{
lblSuccess.Visible = true;
}
else
{
lblFail.Visible = true;
}
}
}
However, its not working and always enables the success label. How do I fix this?
Try it once with the following snippet:
using (LoginDataEntities lg = new LoginDataEntities())
{
string #email = tbUsernameL.Text;
string #password = tbPassL.Text;
var logged = lg.Logins
.SingleOrDefault(l=> l.Username == #email && l.Pass == #password);
if (logged != null) // update
{
lblSuccess.Visible = true;
}
else
{
lblFail.Visible = true;
}
}
Alternatively, can you also look at the following example again:
http://www.c-sharpcorner.com/uploadfile/b19d5a/custom-user-login-and-registration-page-in-Asp-Net-mvc3-with-razor-and-entity-framework/
Or you refactorisiers the VS template with Individual User Accounts

How to change this statement to detached criteria

This statement is to check if the user is existing in the database.
public boolean isExisting(int userId) {
String sql = "{call isExistingUser(?)}";
Session session = null;
boolean isExisting = false;
try {
session = getSession();
SQLQuery query = session.createSQLQuery(sql);
query.setParameter(0, userId);
List<?> list = query.list();
isExisting = list.get(0) != null ? (Boolean) list.get(0) : false;
} finally {
if (session != null)
session.close();
}
return isExisting;
}
This is the stored procedure:
CREATE DEFINER=cbsadmin#% PROCEDURE isExistingUser(IN userId int)
BEGIN
SELECT USER_ID FROM USER_LOGIN_STATUS WHERE USER_ID = userId;
END
It is not possible to query a Stored procedure using detached criteria in NHibernate.
You need to use SQL query only.
See here.

SQL aspnet_profile

any idea how I can get user FirstName and LastName from the aspnet_profile table based on UserID using SQL becasue I would like to use in Telerik Reporting as a user parameter.
Sample row (FirstName is George, LastName is Test):
UserID: 06b24b5c-9aa1-426e-b7e4-0771c5f85e85
PropertyName: MobilePhone:S:0:0:Initials:S:0:1:City:S:1:14:FirstName:S:15:6:PostalCode:S:21:7:‌​WorkPhone:S:28:12:LastName:S:40:5:Address1:S:45:17:Address2:S:62:0:Province:S:62:‌​2:Organization:S:64:4:ClinicId:S:68:1:Country:S:69:6:Fax:S:75:0:MSPNumber:S:75:0:‌​
PropertyValuesString: HEast HustonEASGeorgeT7D 1N8604-111-2222Test5555 Beddtvue AveDCHCNL2Canada
PropertyValuesBinary: <Binary data>
LastUpdateDate: 2010-01-02 22:22:03.947
If you insist on using SQL, I'm sure a large number of SUBSTRINGs and PATINDEXes will get you there but it won't be a clean solution.
Update: user373721 found a great resource and posted a comment about it, but it can be easily missed, so I decided to add it to the answer, too - How to get asp.net profile value from MS SQL database using T-SQL?
The built-in dbo.aspnet_Profile_GetProperties stored procedure returns the PropertyValuesString value that is later parsed in the ParseDataFromDB
function.
private void GetPropertyValuesFromDatabase(string userName, SettingsPropertyValueCollection svc)
{
if (HostingEnvironment.IsHosted && EtwTrace.IsTraceEnabled(4, 8))
{
EtwTrace.Trace(EtwTraceType.ETW_TYPE_PROFILE_BEGIN, HttpContext.Current.WorkerRequest);
}
HttpContext current = HttpContext.Current;
string[] names = null;
string values = null;
byte[] buffer = null;
if (current != null)
{
if (!current.Request.IsAuthenticated)
{
string anonymousID = current.Request.AnonymousID;
}
else
{
string name = current.User.Identity.Name;
}
}
try
{
SqlConnectionHolder connection = null;
SqlDataReader reader = null;
try
{
connection = SqlConnectionHelper.GetConnection(this._sqlConnectionString, true);
this.CheckSchemaVersion(connection.Connection);
SqlCommand command = new SqlCommand("dbo.aspnet_Profile_GetProperties", connection.Connection) {
CommandTimeout = this.CommandTimeout,
CommandType = CommandType.StoredProcedure
};
command.Parameters.Add(this.CreateInputParam("#ApplicationName", SqlDbType.NVarChar, this.ApplicationName));
command.Parameters.Add(this.CreateInputParam("#UserName", SqlDbType.NVarChar, userName));
command.Parameters.Add(this.CreateInputParam("#CurrentTimeUtc", SqlDbType.DateTime, DateTime.UtcNow));
reader = command.ExecuteReader(CommandBehavior.SingleRow);
if (reader.Read())
{
names = reader.GetString(0).Split(new char[] { ':' });
values = reader.GetString(1);
int length = (int) reader.GetBytes(2, 0L, null, 0, 0);
buffer = new byte[length];
reader.GetBytes(2, 0L, buffer, 0, length);
}
}
finally
{
if (connection != null)
{
connection.Close();
connection = null;
}
if (reader != null)
{
reader.Close();
}
}
ProfileModule.ParseDataFromDB(names, values, buffer, svc);
if (HostingEnvironment.IsHosted && EtwTrace.IsTraceEnabled(4, 8))
{
EtwTrace.Trace(EtwTraceType.ETW_TYPE_PROFILE_END, HttpContext.Current.WorkerRequest, userName);
}
}
catch
{
throw;
}
}
internal static void ParseDataFromDB(string[] names, string values, byte[] buf, SettingsPropertyValueCollection properties)
{
if (((names != null) && (values != null)) && ((buf != null) && (properties != null)))
{
try
{
for (int i = 0; i < (names.Length / 4); i++)
{
string str = names[i * 4];
SettingsPropertyValue value2 = properties[str];
if (value2 != null)
{
int startIndex = int.Parse(names[(i * 4) + 2], CultureInfo.InvariantCulture);
int length = int.Parse(names[(i * 4) + 3], CultureInfo.InvariantCulture);
if ((length == -1) && !value2.Property.PropertyType.IsValueType)
{
value2.PropertyValue = null;
value2.IsDirty = false;
value2.Deserialized = true;
}
if (((names[(i * 4) + 1] == "S") && (startIndex >= 0)) && ((length > 0) && (values.Length >= (startIndex + length))))
{
value2.SerializedValue = values.Substring(startIndex, length);
}
if (((names[(i * 4) + 1] == "B") && (startIndex >= 0)) && ((length > 0) && (buf.Length >= (startIndex + length))))
{
byte[] dst = new byte[length];
Buffer.BlockCopy(buf, startIndex, dst, 0, length);
value2.SerializedValue = dst;
}
}
}
}
catch
{
}
}
}
http://www.karpach.com/Get-asp-net-profile-value-MS-SQL-database-using-T-SQL.htm
this helped me tremendously!
following the steps in that link allowed me to fetch any particular data in the PropertyValueString from the aspnet_profile table.
copying and pasting-
First Function:
CREATE FUNCTION dbo.fn_GetElement
(
#ord AS INT,
#str AS VARCHAR(8000),
#delim AS VARCHAR(1) )
RETURNS INT
AS
BEGIN
-- If input is invalid, return null.
IF #str IS NULL
OR LEN(#str) = 0
OR #ord IS NULL
OR #ord < 1
-- #ord > [is the] expression that calculates the number of elements.
OR #ord > LEN(#str) - LEN(REPLACE(#str, #delim, '')) + 1
RETURN NULL
DECLARE #pos AS INT, #curord AS INT
SELECT #pos = 1, #curord = 1
-- Find next element's start position and increment index.
WHILE #curord < #ord
SELECT
#pos = CHARINDEX(#delim, #str, #pos) + 1,
#curord = #curord + 1
RETURN
CAST(SUBSTRING(#str, #pos, CHARINDEX(#delim, #str + #delim, #pos) - #pos) AS INT)
END
Second Function:
CREATE FUNCTION dbo.fn_GetProfileElement
(
#fieldName AS NVARCHAR(100),
#fields AS NVARCHAR(4000),
#values AS NVARCHAR(4000))
RETURNS NVARCHAR(4000)
AS
BEGIN
-- If input is invalid, return null.
IF #fieldName IS NULL
OR LEN(#fieldName) = 0
OR #fields IS NULL
OR LEN(#fields) = 0
OR #values IS NULL
OR LEN(#values) = 0
RETURN NULL
-- locate FieldName in Fields
DECLARE #fieldNameToken AS NVARCHAR(20)
DECLARE #fieldNameStart AS INTEGER,
#valueStart AS INTEGER,
#valueLength AS INTEGER
-- Only handle string type fields (:S:)
SET #fieldNameStart = CHARINDEX(#fieldName + ':S',#Fields,0)
-- If field is not found, return null
IF #fieldNameStart = 0 RETURN NULL
SET #fieldNameStart = #fieldNameStart + LEN(#fieldName) + 3
-- Get the field token which I've defined as the start of the
-- field offset to the end of the length
SET #fieldNameToken = SUBSTRING(#Fields,#fieldNameStart,LEN(#Fields)-#fieldNameStart)
-- Get the values for the offset and length
SET #valueStart = dbo.fn_getelement(1,#fieldNameToken,':')
SET #valueLength = dbo.fn_getelement(2,#fieldNameToken,':')
-- Check for sane values, 0 length means the profile item was
-- stored, just no data
IF #valueLength = 0 RETURN ''
-- Return the string
RETURN SUBSTRING(#values, #valueStart+1, #valueLength)
END
SQL Query can be modded to your needs
SELECT dbo.fn_GetProfileElement('FirstName',PropertyNames,PropertyValuesString)
, dbo.fn_GetProfileElement('LastName',PropertyNames,PropertyValuesString)
FROM aspnet_Profile
For those who are still looking for a method to parse the aspnet_Profile table using pure SQL. Here is what I use:
First you need a Tally table. If you do not know what this is, read this article by Jeff Moden: http://www.sqlservercentral.com/articles/T-SQL/62867/
For you to generate the tally table use this script:
SELECT TOP 11000 IDENTITY(INT,1,1) AS N INTO dbo.Tally FROM Master.dbo.SysColumns sc1, Master.dbo.SysColumns sc2
--===== Add a Primary Key to maximize performance
ALTER TABLE dbo.Tally ADD CONSTRAINT PK_Tally_N PRIMARY KEY CLUSTERED (N) WITH FILLFACTOR = 100
--===== Let the public use it
GRANT SELECT, REFERENCES ON dbo.Tally TO PUBLIC
Now on to parsing the ProfileData:
The process below is the fastest way I found to do this after lots of testing on my specific data. I have tested parsing the complete table in one go, but that runs slower than using the function below and parsing one user at a time with a CROSS APPLY.
So to call the function, use something like:
SELECT bla, bla
FROM aspnet_Users u CROSS APPY dbo.ProfileProperties(u.UserID)
The only thing you need to do is to update 3 things to contain the Profile Properties that you use:
1) the return table
2) the PIVOT statement, and
3) the insert statement copying the data from the PIVOT into the return table
Here is the function, Enjoy!
/** =============================================
** Author: Francois Grobler
** Create date: 2013-04-25
** Description: This function extracts all
** Profile Properties for a given UserId,
** and returns them as a table
** Change History:
** Date: Author: Change:
**
** ============================================= **/
CREATE FUNCTION dbo.ProfileProperties
(
#UserID UNIQUEIDENTIFIER
)
RETURNS #returnTable TABLE(
FirstName nvarchar(200)
, LastName nvarchar(200)
, PassportNumber nvarchar(100)
, PositionCode int
, CellNumber nvarchar(20)
, Telephone nvarchar(30)
, FaxNumber nvarchar(20)
, Email nvarchar(200)
, PersalNumber nvarchar(10)
, SouthAfricanIdentityNumber nchar(13)
, ContractNumber nvarchar(20)
, DepartmentName nvarchar(200)
, SiteName nvarchar(200)
, DepartmentCode int
, SiteCode int
, UserAccessCode int
, ApproverCode int
)
WITH SCHEMABINDING
AS
BEGIN
WITH Properties(PropertyNo, PropertyType, UserId, Value)
AS
(
SELECT (ROW_NUMBER() OVER(ORDER BY UserId) - 1) / 4 PropertyNo
, (ROW_NUMBER() OVER(PARTITION BY p.UserId ORDER BY UserId) - 1) % 4 PropertyType
, p.UserId
, SUBSTRING(':' + CONVERT(nvarchar(4000), p.PropertyNames), n + 1, CHARINDEX(':', ':' + CONVERT(nvarchar(4000), p.PropertyNames), n + 1) - n - 1) Value
FROM dbo.Tally, dbo.aspnet_Profile p
WHERE n < LEN(':' + CONVERT(nvarchar(4000), p.PropertyNames))
and SUBSTRING(':' + CONVERT(nvarchar(4000), p.PropertyNames), n, 1) = ':'
and p.UserId = #UserID
)
, FlatProperties(UserId, Property, ValueType, StartIndex, ValueLength)
AS
(
SELECT UserId
, MAX(CASE WHEN PropertyType = 0 THEN Value ELSE '' END) Property
, MAX(CASE WHEN PropertyType = 1 THEN Value ELSE '' END) ValueType
, MAX(CASE WHEN PropertyType = 2 THEN CONVERT(int, Value) + 1 ELSE 0 END) StartIndex
, MAX(CASE WHEN PropertyType = 3 THEN CONVERT(int, Value) ELSE 0 END) ValueLength
FROM
Properties
GROUP BY UserID, PropertyNo
)
, PropertyValues(UserID, PropertyName, PropertyValue)
AS
(
SELECT p.UserID, fp.Property
, CASE fp.ValueType
WHEN 'S' THEN SUBSTRING(p.PropertyValuesString, fp.StartIndex, fp.ValueLength)
ELSE SUBSTRING(p.PropertyValuesBinary, fp.StartIndex, fp.ValueLength) END Value
FROM dbo.aspnet_Profile p INNER JOIN flatProperties fp ON p.UserId = fp.UserId
WHERE p.UserId = #UserID
)
, PropertyTable
AS
(
SELECT
UserID
, pvt.[FirstName]
, pvt.[LastName]
, pvt.[PassportNumber]
, pvt.[PositionCode]
, pvt.[CellNumber]
, pvt.[Telephone]
, pvt.[FaxNumber]
, pvt.[Email]
, pvt.[PersalNumber]
, pvt.[SouthAfricanIdentityNumber]
, pvt.[ContractNumber]
, pvt.[DepartmentName]
, pvt.[SiteName]
, pvt.[DepartmentCode]
, pvt.[SiteCode]
, pvt.[UserCode] UserAccessCode
, pvt.[ApproverCode]
FROM PropertyValues
PIVOT (
MAX(PropertyValue) FOR PropertyName IN ([FirstName],[LastName],[PassportNumber],[PositionCode],[CellNumber],[Telephone],[FaxNumber],[Email],[PersalNumber],[SouthAfricanIdentityNumber],[ContractNumber],[DepartmentName],[SiteName],[DepartmentCode],[SiteCode],[UserCode],[ApproverCode])
) AS pvt
)
INSERT INTO #returnTable
(
FirstName
, LastName
, PassportNumber
, PositionCode
, CellNumber
, Telephone
, FaxNumber
, Email
, PersalNumber
, SouthAfricanIdentityNumber
, ContractNumber
, DepartmentName
, SiteName
, DepartmentCode
, SiteCode
, UserAccessCode
, ApproverCode
)
SELECT TOP 1
FirstName
, LastName
, PassportNumber
, PositionCode
, CellNumber
, Telephone
, FaxNumber
, Email
, PersalNumber
, SouthAfricanIdentityNumber
, ContractNumber
, DepartmentName
, SiteName
, DepartmentCode
, SiteCode
, UserAccessCode
, ApproverCode
FROM PropertyTable;
RETURN;
END
GO