Transfer login from SQL2014 to SQL2008R2 - sql-server-2008

I use a stored procedure called sp_help_revlogin to transfer my SQL2014 logins to SQL2008R2.
I usually use it to transfer logins between SQL2008R2 and it works fine but today i have to transfer 2 logins from 2014 to 2008R2 and i receive this error message :
Msg 15021, Level 16, State 2, Line 2 Invalid value given for parameter
PASSWORD. Specify a valid parameter value.
sp_help_revlogin content ==>
USE [master]
GO
/****** Object: StoredProcedure [dbo].[sp_help_revlogin] Script Date: 31/08/2016 11:38:19 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_help_revlogin] #login_name sysname = NULL AS
DECLARE #name sysname
DECLARE #type varchar (1)
DECLARE #hasaccess int
DECLARE #denylogin int
DECLARE #is_disabled int
DECLARE #PWD_varbinary varbinary (256)
DECLARE #PWD_string varchar (514)
DECLARE #SID_varbinary varbinary (85)
DECLARE #SID_string varchar (514)
DECLARE #tmpstr varchar (1024)
DECLARE #is_policy_checked varchar (3)
DECLARE #is_expiration_checked varchar (3)
DECLARE #defaultdb sysname
IF (#login_name IS NULL)
DECLARE login_curs CURSOR FOR
SELECT p.sid, p.name, p.type, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin FROM
sys.server_principals p LEFT JOIN sys.syslogins l
ON ( l.name = p.name ) WHERE p.type IN ( 'S', 'G', 'U' ) AND p.name <> 'sa'
ELSE
DECLARE login_curs CURSOR FOR
SELECT p.sid, p.name, p.type, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin FROM
sys.server_principals p LEFT JOIN sys.syslogins l
ON ( l.name = p.name ) WHERE p.type IN ( 'S', 'G', 'U' ) AND p.name = #login_name
OPEN login_curs
FETCH NEXT FROM login_curs INTO #SID_varbinary, #name, #type, #is_disabled, #defaultdb, #hasaccess, #denylogin
IF (##fetch_status = -1)
BEGIN
PRINT 'No login(s) found.'
CLOSE login_curs
DEALLOCATE login_curs
RETURN -1
END
SET #tmpstr = '/* sp_help_revlogin script '
PRINT #tmpstr
SET #tmpstr = '** Generated ' + CONVERT (varchar, GETDATE()) + ' on ' + ##SERVERNAME + ' */'
PRINT #tmpstr
PRINT ''
WHILE (##fetch_status <> -1)
BEGIN
IF (##fetch_status <> -2)
BEGIN
PRINT ''
SET #tmpstr = '-- Login: ' + #name
PRINT #tmpstr
IF (#type IN ( 'G', 'U'))
BEGIN -- NT authenticated account/group
SET #tmpstr = 'CREATE LOGIN ' + QUOTENAME( #name ) + ' FROM WINDOWS WITH DEFAULT_DATABASE = [' + #defaultdb + ']'
END
ELSE BEGIN -- SQL Server authentication
-- obtain password and sid
SET #PWD_varbinary = CAST( LOGINPROPERTY( #name, 'PasswordHash' ) AS varbinary (256) )
EXEC sp_hexadecimal #PWD_varbinary, #PWD_string OUT
EXEC sp_hexadecimal #SID_varbinary,#SID_string OUT
-- obtain password policy state
SELECT #is_policy_checked = CASE is_policy_checked WHEN 1 THEN 'ON' WHEN 0 THEN 'OFF' ELSE NULL END FROM sys.sql_logins WHERE name = #name
SELECT #is_expiration_checked = CASE is_expiration_checked WHEN 1 THEN 'ON' WHEN 0 THEN 'OFF' ELSE NULL END FROM sys.sql_logins WHERE name = #name
SET #tmpstr = 'CREATE LOGIN ' + QUOTENAME( #name ) + ' WITH PASSWORD = ' + #PWD_string + ' HASHED, SID = ' + #SID_string + ', DEFAULT_DATABASE = [' + #defaultdb + ']'
IF ( #is_policy_checked IS NOT NULL )
BEGIN
SET #tmpstr = #tmpstr + ', CHECK_POLICY = ' + #is_policy_checked
END
IF ( #is_expiration_checked IS NOT NULL )
BEGIN
SET #tmpstr = #tmpstr + ', CHECK_EXPIRATION = ' + #is_expiration_checked
END
END
IF (#denylogin = 1)
BEGIN -- login is denied access
SET #tmpstr = #tmpstr + '; DENY CONNECT SQL TO ' + QUOTENAME( #name )
END
ELSE IF (#hasaccess = 0)
BEGIN -- login exists but does not have access
SET #tmpstr = #tmpstr + '; REVOKE CONNECT SQL TO ' + QUOTENAME( #name )
END
IF (#is_disabled = 1)
BEGIN -- login is disabled
SET #tmpstr = #tmpstr + '; ALTER LOGIN ' + QUOTENAME( #name ) + ' DISABLE'
END
PRINT #tmpstr
END
FETCH NEXT FROM login_curs INTO #SID_varbinary, #name, #type, #is_disabled, #defaultdb, #hasaccess, #denylogin
END
CLOSE login_curs
DEALLOCATE login_curs
RETURN 0
someone can help me please ?
THanks a lot.
Ludo

Related

how to convert MSSQL Stored Procedure to Mysql SP using 'With' Keyword in mysql?

this is my Stored Procedure of MSSQL and I want to be convert this SP to MySQL,but i cant understand what to used instead of 'with' keyword in MySql so any one help me ??? Thanx in Advance!!!!!
ALTER PROCEDURE [dbo].[TPortRateOnKm_SP_List]
#qtype varchar(MAX) = NULL,
#query varchar(MAX)= NULL,
#Sortname varchar(MAX) =NULL,
#sortorder varchar(MAX) =NULL ,
#PageNo int,
#RecordsPerPage int,
#likesearch int
AS
BEGIN
DECLARE #temp INT;
DECLARE #qry varchar(MAX) = '';
DECLARE #qry1 varchar(MAX) = '';
set #temp = (#PageNo - 1) * #RecordsPerPage
If #query is not null and #query <> ''
Begin
Set #qry1= ' Where '+ #query
End
Else
Begin
Set #qry1= ''
End
If #query is not null and #query <> ''
Begin
Set #query= ' Where ('+ #query + ') and (RowNo > ' +
cast(((#PageNo-1) *#RecordsPerPage) as varchar) + ' AND RowNo <= '
+ cast(#PageNo * #RecordsPerPage as varchar)+ ') '
End
Else
Begin
Set #query= ' Where (RowNo > ' + cast(((#PageNo-1) *#RecordsPerPage) as varchar) + ' AND RowNo <= ' + cast(#PageNo * #RecordsPerPage as varchar)+ ') '
End
If #sortorder is not null and #sortorder <> ''
Begin
Set #sortorder= ' Order By '+ #sortorder
End
Else
Begin
Set #sortorder= ' ORDER BY VehicleId'
End
here is 'With' Keyword
set #qry = 'Set dateformat dmy ;WITH CustomTable as
(Select ROW_NUMBER() OVER ( '+ #sortorder+') as RowNo,A.* From
(select Convert(varchar,T.WefDate,103) As WefDate,T.VehicleId as
VehicleId,V.VehicleNo as VehicleNo,T.StartKm,T.EndKm,T.Rate from
TrnRateOnKmRange T left outer join MasterVehicle V on
V.VehicleId=T.VehicleId) A '
Set #qry= #qry + #qry1
Set #qry= #qry + ' )
SELECT CAST(RowNo AS INT) as
RowNo,WefDate,VehicleId,VehicleNo,StartKm,EndKm,Rate
FROM CustomTable'
Set #qry=#qry + #query + #sortorder
EXECUTE (#qry)
END
can any one do this ??

MySQL if Execution Exists

i try to set an Existence when there is a data found in my table(user). In the query, i need to use a variable. However, in the IF case, it doesn't work. Any ideal? or any alternative way to do it?
DECLARE
#myTableName varchar(1000),
#myQuery varchar(1000)
SET #myTableName = userTable //the userTable i will retrieve from a parameter
SET #myQuery = 'SELECT TOP 1 1 FROM ' + #myTableName + ''
IF EXISTS (EXEC(#myQuery))
BEGIN
print('Success')
END
ELSE
BEGIN
print('Failed')
END
SET #myQuery = 'SELECT * FROM ' + #myTableName + ' LIMIT 0,1 '
If it doesn't work, you can try this approach :
DECLARE
#myTableName varchar(1000),
#i int
SET #myTableName = userTable //the userTable i will retrieve from a parameter
SET #i = 'SELECT count(*) AS cnt FROM ' + #myTableName + ''
IF #i > 0
BEGIN
print('Success')
END
ELSE
BEGIN
print('Failed')
END

tsql dynamic sql best approach

The dynamica query I have below works but wondering if what I have below can be optimized or if there is a better way of doing it.
I have a web form where the user enters a location and Date Collected. For the date collected, I have a From Date Collected and To Date Collected. The user an leave the To date collected blank in which case it will do anything greater than the From Date Collected.
Note how I am doing the IS NOT NULL and 1=1 below. Also wondering if a dynamic SQL is the best approach or if there is a simpler way of doing this.
DECLARE #sql varchar(max);
SET #sql = 'SELECT * from tblProgram WHERE 1=1'
IF (#Location IS NOT NULL)
BEGIN
SET #sql = #sql + ' AND Location = ' + #Location
END
IF (#FromDateCollected IS NOT NULL AND #ToDateCollected IS NOT NULL)
BEGIN
SET #sql = #sql + ' AND pw.DateCollected >= ' + QUOTENAME(convert(varchar, #FromDateCollected,101),'''')
+ ' AND pw.DateCollected <= ' + QUOTENAME(convert(varchar, #ToDateCollected,101),'''')
END
ELSE IF (#FromDateCollected IS NOT NULL AND #ToDateCollected IS NULL)
BEGIN
SET #sql = #sql + ' AND pw.DateCollected >= ' + QUOTENAME(convert(varchar, #FromDateCollected,101),'''')
END
exec(#sql)
Well you can do as ta.speot.is comments use static SQL and do
WHERE x is null or x > date_column?
However if you insist on using Dynamic SQL you should use a parameterized SQL statement using sp_executeSQL
Its easier to read, you don't have to use quotename, and you're protected from SQL Injection
DECLARE #Location int
DECLARE #FromDateCollected datetime
DECLARE #ToDateCollected datetime
SET #ToDateCollected = '1/02/2012'
DECLARE #sql nvarchar(max)
DECLARE #ParmDefinition nvarchar(max)
SET #ParmDefinition = N'#Location int , #FromDateCollected datetime, #ToDateCollected datetime ';
SET #sql = N'SELECT * from tblProgram WHERE 1=1'
IF (#Location IS NOT NULL)
BEGIN
SET #sql = #sql + N' AND Location = #Location'
END
IF (#FromDateCollected IS NOT NULL AND #ToDateCollected IS NOT NULL)
BEGIN
SET #sql = #sql + N' AND pw.DateCollected >= #FromDateCollected '
+ N' AND pw.DateCollected <= #ToDateCollected '
END
ELSE IF (#FromDateCollected IS NOT NULL AND #ToDateCollected IS NULL)
BEGIN
SET #sql = #sql + N' AND pw.DateCollected >= #FromDateCollected'
END
exec sp_executesql #SQL, #ParmDefinition, #Location = #Location,
#FromDateCollected = #FromDateCollected,
#ToDateCollected = #ToDateCollected
DEMO

How to get all the transaction logs (insert update delete) for a specific table in SQL Server 2008

I want to get all the transactions applied on a specific table in SQL Server 2008.
I found the last time a table was updated using this script:
SELECT OBJECT_NAME(OBJECT_ID) AS DatabaseName, last_user_update,*
FROM sys.dm_db_index_usage_stats
WHERE database_id = DB_ID( 'DBName')
AND OBJECT_ID=OBJECT_ID('tableName')
I want to know all the transactions (Inserts, Updates, Deletes) for that table, and their datetime, and the query applied.
What is the best way to do this?
The only way to do this in a reasonable amount of time is to use a third party tool(as Martin said in first comment) such as ApexSQL Log that can read transaction log and get the information you need.
Note that in order for this to work your database has to be in a full recovery mode because that’s when SQL Server logs full transaction details that can be reconstructed later.
Another option is to investigate how to use undocumented fn_dblog function but this will take you a lot more time and you won’t be able to read detached logs or transaction log backups.
creating a trigger which will create a new table Emp_audit and add new tuples to it whenever any change is made to table employee
create trigger my_trigger on Employees
AFTER INSERT, UPDATE, DELETE
AS
DECLARE #What varchar(30);
DECLARE #Who varchar(30);
DECLARE #for int;
DECLARE #At time;
DECLARE #COUNTI int;
DECLARE #COUNTD int;
select #COUNTI = COUNT(*) from inserted;
select #COUNTD = COUNT(*) from deleted;
set #Who = SYSTEM_USER;
set #At = CURRENT_TIMESTAMP;
if( #COUNTD = 0 and #COUNTI = 1)
begin
set #What = 'insert';
select #for = EmployeeID from inserted i;
end
else
begin
if( #COUNTD = 1 and #COUNTI = 0)
begin
set #What = 'delete';
select #for = EmployeeID from deleted i;
end
else
begin
set #What = 'update';
select #for = EmployeeID from inserted i;
end
end
INSERT INTO EMP_Audit Values (#What, #Who, #for, #At);
You would be much better off setting up auditing for this need rather than trying to extract this information retrospectively from the transaction log.
If you are on Enterprise Edition you could use the built in SQL Server Audit functionality, otherwise it should be relative straight forward to log the desired information via triggers.
You could create your own transaction logs
Step 1: Create your own table for transaction logs
CREATE TABLE [dbo].[TransactionLogs](
[TransactionLogID] [bigint] IDENTITY(1,1) NOT NULL,
[Query] [nvarchar](max) NOT NULL,
[DateCreated] [datetime] NOT NULL,
CONSTRAINT [PK_TransactionLogs] PRIMARY KEY CLUSTERED
(
[TransactionLogID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
Step 2: Create stored procedure that create logs. (Note: Replace YourTablePKColumn with your table primary key column.)
create procedure [dbo].[sp_CreateQueryLogs]
(
#Query nvarchar(max) = null output,
#TableName nvarchar(100),
#YourTablePKColumn nvarchar(30),
#QueryTypeID tinyint --0 insert, 1 update, 2 delete
)
as
begin
declare #object_id bigint, #column_name nvarchar(100), #collation_name nvarchar(50), #column_name_id nvarchar(100) = null, #column_names nvarchar(max) = '', #column_values nvarchar(max) = '', #column_names_create nvarchar(max) = '', #values nvarchar(max) = '', #user_type_id int, #max_length nvarchar(10), #type_name nvarchar(50), #CreateTempTable nvarchar(max) = '', #is_nullable bit, #value nvarchar(max) = ''
create table #tmpValues(ColumnValues nvarchar(max))
insert into #tmpValues(ColumnValues)
exec('select CAST ( ( select * from ' + #TableName + ' where YourTablePKColumn = ' + #YourTablePKColumn + '
FOR XML PATH(''tr''), TYPE
) AS NVARCHAR(MAX) )')
select #values = ColumnValues from #tmpValues
if #QueryTypeID = 0 --insert
set #Query = 'insert into ' + #TableName + '('
else if #QueryTypeID = 1 --update
set #Query = 'update ' + #TableName + ' set '
else if #QueryTypeID = 2 --dalete
set #Query = 'delete ' + #TableName + ' '
select #object_id = object_id from sys.tables where name = #TableName
if not cursor_status('local','columnCursor') <= -1
begin
close columnCursor;
deallocate columnCursor;
end
declare columnCursor cursor local for
select name, user_type_id, convert(nvarchar(10), max_length), is_nullable from sys.columns where object_id = #object_id order by column_id ;
open columnCursor;
fetch next from columnCursor
into #column_name, #user_type_id, #max_length, #is_nullable;
while ##FETCH_STATUS = 0
begin
select #type_name = name, #collation_name = collation_name from sys.types where user_type_id = #user_type_id
if #column_name_id is null
set #column_name_id = #column_name
else
begin
set #column_names += #column_name + ', '
declare #value_keys_start nvarchar(max) = '<' + #column_name + '>', #value_keys_end nvarchar(max) = '</' + #column_name + '>'
if charindex(#value_keys_start,#values,1) = 0
begin
if #QueryTypeID = 0 --insert
set #column_values += 'null,'
else if #QueryTypeID = 1 --update
set #column_values += #column_name + ' = null,'
end
else
begin
if #QueryTypeID = 0 --insert
if #collation_name is null and not (#type_name like '%date%' or #type_name like '%time%')
set #column_values += substring(#values, charindex(#value_keys_start,#values,1) + len(#value_keys_start), charindex(#value_keys_end,#values,1) - (charindex(#value_keys_start,#values,1) + len(#value_keys_start))) + ','
else if #type_name like '%date%' or #type_name like '%time%'
set #column_values += '''' + replace(substring(#values, charindex(#value_keys_start,#values,1) + len(#value_keys_start), charindex(#value_keys_end,#values,1) - (charindex(#value_keys_start,#values,1) + len(#value_keys_start))),'T',' ') + ''','
else
set #column_values += '''' + replace(substring(#values, charindex(#value_keys_start,#values,1) + len(#value_keys_start), charindex(#value_keys_end,#values,1) - (charindex(#value_keys_start,#values,1) + len(#value_keys_start))),'''','''''') + ''','
else if #QueryTypeID = 1 --update
if #collation_name is null and not (#type_name like '%date%' or #type_name like '%time%')
set #column_values += #column_name + '=' + substring(#values, charindex(#value_keys_start,#values,1) + len(#value_keys_start), charindex(#value_keys_end,#values,1) - (charindex(#value_keys_start,#values,1) + len(#value_keys_start))) + ','
else if #type_name like '%date%' or #type_name like '%time%'
set #column_values += #column_name + '=' + '''' + replace(substring(#values, charindex(#value_keys_start,#values,1) + len(#value_keys_start), charindex(#value_keys_end,#values,1) - (charindex(#value_keys_start,#values,1) + len(#value_keys_start))),'T',' ') + ''','
else
set #column_values += #column_name + '=' + '''' + replace(substring(#values, charindex(#value_keys_start,#values,1) + len(#value_keys_start), charindex(#value_keys_end,#values,1) - (charindex(#value_keys_start,#values,1) + len(#value_keys_start))),'''','''''') + ''','
end
end
fetch next from columnCursor
into #column_name, #user_type_id, #max_length, #is_nullable;
end
if not cursor_status('local','columnCursor') <= -1
begin
close columnCursor;
deallocate columnCursor;
end
if #QueryTypeID = 0 --insert
set #Query += substring(#column_names,1,len(#column_names) - 1) + ')
values (' + substring(#column_values,1,len(#column_values) - 1) + ')'
else if #QueryTypeID = 1 --update or delete
set #Query += substring(#column_values,1,len(#column_values) - 1) + ' where YourTablePKColumn = ' + #YourTablePKColumn
else
set #Query += ' where YourTablePKColumn = ' + #YourTablePKColumn
end
Step 3: Created trigger to table you want to have transaction logs
CREATE TRIGGER trg_MyTrigger ON YouTableName
AFTER INSERT, DELETE, UPDATE
AS
BEGIN
SET NOCOUNT ON;
declare #TableName nvarchar(100) = 'YouTableName', #Query nvarchar(max), #QueryTypeID tinyint, #YourTablePKColumn nvarchar(30)
if exists(select * from deleted) and exists(select * from inserted)
begin
set #QueryTypeID = 1
if not cursor_status('local','updatedCursor') <= -1
begin
close updatedCursor;
deallocate updatedCursor;
end
declare updatedCursor cursor local for
select cast(YourTablePKColumn as nvarchar(30)) from inserted;
open updatedCursor;
fetch next from updatedCursor
into #YourTablePKColumn;
while ##FETCH_STATUS = 0
begin
exec dbo.sp_CreateQueryLogs #Query = #Query output, #TableName = #TableName, #YourTablePKColumn = #YourTablePKColumn, #QueryTypeID = #QueryTypeID
insert into TransactionLogs
(Query, DateCreated)
values (#Query,getdate())
fetch next from updatedCursor
into #YourTablePKColumn;
end
if not cursor_status('local','updatedCursor') <= -1
begin
close updatedCursor;
deallocate updatedCursor;
end
end
else if exists(select * from deleted) and not exists(select * from inserted)
begin
set #QueryTypeID = 2
if not cursor_status('local','deletedCursor') <= -1
begin
close deletedCursor;
deallocate deletedCursor;
end
declare deletedCursor cursor local for
select cast(YourTablePKColumn as nvarchar(30)) from deleted;
open deletedCursor;
fetch next from deletedCursor
into #YourTablePKColumn;
while ##FETCH_STATUS = 0
begin
exec dbo.sp_CreateQueryLogs #Query = #Query output, #TableName = #TableName, #YourTablePKColumn = #YourTablePKColumn, #QueryTypeID = #QueryTypeID
insert into TransactionLogs
(Query, DateCreated)
values (#Query,getdate())
fetch next from deletedCursor
into #YourTablePKColumn;
end
if not cursor_status('local','deletedCursor') <= -1
begin
close deletedCursor;
deallocate deletedCursor;
end
end
else
begin
set #QueryTypeID = 0
if not cursor_status('local','insertedCursor') <= -1
begin
close insertedCursor;
deallocate insertedCursor;
end
declare insertedCursor cursor local for
select cast(YourTablePKColumn as nvarchar(30)) from inserted;
open insertedCursor;
fetch next from insertedCursor
into #YourTablePKColumn;
while ##FETCH_STATUS = 0
begin
exec dbo.sp_CreateQueryLogs #Query = #Query output, #TableName = #TableName, #YourTablePKColumn = #YourTablePKColumn, #QueryTypeID = #QueryTypeID
insert into TransactionLogs
(Query, DateCreated)
values (#Query,getdate())
fetch next from insertedCursor
into #YourTablePKColumn;
end
if not cursor_status('local','insertedCursor') <= -1
begin
close insertedCursor;
deallocate insertedCursor;
end
end
END
GO

Conversion failed when converting the nvarchar value 'SELECT * FROM

Using Sql Server 2008, developed a view "vw_MasterView" I get this error:
Conversion failed when converting the nvarchar value 'SELECT * FROM VW_MASTERVIEW v WHERE 1=1 AND v.ClinicId = '' to data type int.
when I run the following stored procedure:
USE [xxxxxxx]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[ClientSummary1]
#LocationId int,
#startDate datetime,
#endDate datetime,
#userName nvarchar(50)
AS
declare #sql nvarchar(2000);
SET NOCOUNT ON
set #sql = 'SELECT * FROM VW_MASTERVIEW v WHERE 1=1 ';
IF #LocationId is not null
begin
set #sql = #sql + ' AND v.LocationId = ''' + #LocationId + '''';
end
if #userName is not null
begin
set #sql = #sql + ' AND (v.FirstUser = '' OR v.SecondUser = '')' + #userName + '''';
end
if #startDate is not null
begin
set #sql = #sql + ' AND v.FirstVisitDate = ''' + #startDate + '''';
end
if #endDate is not null
begin
set #sql = #sql + ' AND v.LastVisitDate = ''' + #endDate + '''';
end
EXEC(#sql)
I get both the LocationId and userName from a VS2010 application.
Thanks in Advance
When appending strings together in SQL Server, you have to cast non-textual types (such as int) to a textual type (such as varchar):
set #sql = #sql + ' AND v.LocationId = ''' +
cast(#LocationId as varchar(10)) + '''';
-- ^^^^ have to cast ^^ make sure size is big enough
Note that dynamic SQL should not be necessary in the first place. You can just run the query directly with the parameters (I implemented the null checks with the extra or conditions):
SELECT * FROM VW_MASTERVIEW v
WHERE (v.LocationId = #LocationId OR #LocationId is null)
AND (v.FirstUser = #userName OR v.Seconduser = #userName OR #userName is null)
AND (v.FirstVisitDate = #startDate OR #startDate is null)
AND (v.LastVisitDate = #endDate OR #endDate is null)
I may not have the logic right for FirstUser and SecondUser - I took an educated guess from your incomplete code.
Hope this helps!