Find a dynamic database name in a view - sql

In a SQL server database I have have this kind of table :
EVENT201201.dbo.EvAcc
EVENT201202.dbo.EvAcc
EVENT201203.dbo.EvAcc
EVENT201204.dbo.EvAcc
EVENT201205.dbo.EvAcc
...
EVENTYYYYMM.dbo.EvAcc
I also have DBACCES1.dbo.*. I created a query like this :
DECLARE #SQL varchar(511)
DECLARE #SQL_ACC varchar(250)
DECLARE #SQL_UClass varchar(250)
DECLARE #SQL_UClassDef varchar(250)
DECLARE #TABLENAME varchar(250)
SELECT #TABLENAME ='EVENT' + CONVERT(char(4), YEAR(GETDATE() - 1)) + CONVERT(char(2), MONTH(GETDATE() - 1))
SELECT #SQL_ACC = '' + QuoteName(#TABLENAME) + '.dbo.EvAcc ON EvAcc.fpointeur = cards.fpointeur'
SELECT #SQL_UClass = 'dbacces1.dbo.UClass ON UClass.fpointeur = cards.fpointeur AND MClass = 1'
SELECT #SQL_UClassDef = 'dbacces1.dbo.UClassDef ON UClassDef.SClass = UClass.SClass'
SELECT #SQL = 'SELECT cards.FPointeur, Ref, Peri, cout, cin, edate FROM dbacces1.dbo.cards INNER JOIN ' + #SQL_ACC + ' INNER JOIN ' + #SQL_UClass
EXEC(#SQL)
It works but I need to put this in a view. It doesn't work. I have this error (sorry for the french) :
La construction ou l'instruction SQL Déclarer un curseur n'est pas prise en charge.
It say than declare is not allowed in a view.
I tried to create a valued table function and it doen't work because I can't use the function exec with a string in a function. I have this error :
Invalid use of side-effecting or time-dependent operator in 'EXECUTE STRING' within a function.
I also tried to create procedure but I can't use it as a table in my query. I tried to create a function which return the database name only but it doen't work again.
Do you have any solution?
EDIT 1 - Solution
I create a script executed each month. This is my script :
DECLARE #start_date DATETIME
DECLARE #end_date DATETIME
DECLARE #sql VARCHAR(MAX)
DECLARE #dbname VARCHAR(15)
DECLARE #year VARCHAR(4)
DECLARE #month VARCHAR(2)
--Start the SQL request
SET #sql = 'CREATE VIEW dbo.viewEvAcc AS '
SET #start_date = convert(DATETIME, '01/01/2011', 101)
SET #end_date = GETDATE()
--Loop from the start date to now
WHILE #start_date < #end_date
BEGIN
--Find new year and month
SET #year = CONVERT(CHAR(4), YEAR(#start_date))
SET #month = RIGHT('0' + RTRIM(MONTH(#start_date)), 2);
--Create the db name with year and month
SET #dbname = 'EVENT' + #year + #month
--Concat the SQL Request
SET #sql = #sql + 'SELECT * FROM ' + #dbname + '.dbo.EvAcc'
--Update the start date for the month after
SET #start_date = CONVERT(VARCHAR(8),DATEADD(MONTH,1,CONVERT(VARCHAR(8),#start_date,112)),112)
--If the date is not the last date, it add a union
IF #start_date < #end_date
BEGIN
SET #sql = #sql + ' UNION ALL '
END
END
-- drop and create a view with new information
USE dbacces1
IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'viewEvAcc')DROP VIEW dbo.viewEvAcc;
EXEC(#sql)

If the view changes periodically (i.e. every day), you could script a job to drop and recreate (alter) the view as needed. It could be part of the job that creates and/or populates that new table.
If the empty tables are created ahead of time, look into creating a partitioned view to union them all. You can then put your query with a join on top of that and add the date constraint to that query's where clause.
Partitioned Views

A view can only contain a static select statement, so as a result if you want to implement dynamic SQL then you will need place this inside of a stored procedure. You could follow these steps:
Create Stored Procedure with your dynamic sql - sp_1
Create stored procedure with your other query - sp_2
Inside this stored procedure, call your sp_1 and insert the results into a temp table
Join this temp table with in your query.

Perhaps you could use synonims : http://msdn.microsoft.com/en-us/library/ms187552(v=sql.100).aspx
You make a static view, which selects data from the synonym object, but before selecting from the view you reassign synonym to the table from the database you need.

Related

SQL Server trigger locks table

I'm not very versed in writing SQL, but I'm attempting to implement a trigger after INSERT.
The trigger will fire, however, the more complicated this particular SELECT statement gets it seems that it locks the table. If I simplify the statement to only have a couple of argument it runs fine.
If I run the SELECT statement from SSMS, it works, and if I run it from Visual Studio in the VB.net application, it also works. I've tried increasing the command timeout but it still fails. There are only a few rows of data in the table.
Any insight would be greatly appreciated. Please let me know if more detail is required. This is a payroll timeclock table, I'm triggering when a record is inserted and then grabbing the previous days clock out date (so I can compare the previous days payroll hours to the previous days project hours).
I forgot to mention that I'm running the SELECT statement on a view I created that includes two tables from the native application.
SELECT TOP 1
#LastDateClockOut = datEvent
FROM
MyTable.dbo.TimeMTSView
WHERE
datEvent < #Today
AND strUniqueID = #nUser
AND blnEventType = 0
AND lngClassificationID = 0
ORDER BY
datEvent DESC;
Here is the entire trigger:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[trgAfterInsert]
ON [dbo].[tblTimes]
AFTER INSERT
AS
DECLARE #trg_lngClassificationID int;
SELECT #trg_lngClassificationID = i.lngClassificationID
FROM inserted i;
IF (#trg_lngClassificationID = 0) --If the date Classification is 0 this means it is NORMAL time as opposed to VACATION or HOLIDAY
DECLARE #trg_lngID int;
DECLARE #trg_lngEmployeeID varchar(20);
DECLARE #trg_blnEventType bit;
DECLARE #blnEventChar nvarchar(1);
DECLARE #trg_datEvent datetime;
DECLARE #nUser varchar(255);
DECLARE #cmd varchar(255);
DECLARE #Today varchar(255);
DECLARE #LastDateClockOut datetime;
DECLARE #strLastClock varchar(255);
SELECT #trg_lngID = i.lngID FROM inserted i;
SELECT #trg_lngEmployeeID = i.lngEmployeeID FROM inserted i;
SELECT #trg_blnEventType = i.blnEventType FROM inserted i;
SELECT #trg_datEvent = i.datEvent FROM inserted i;
SELECT #nUser = strUniqueID
FROM dbo.tblEmployees
WHERE lngID = #trg_lngEmployeeID AND blnDeleted = 0
SELECT #blnEventChar = CONVERT(NVARCHAR, #trg_blnEventType);
SELECT #Today = CONVERT(VARCHAR, GetDate(), 103);
SELECT TOP 1 #LastDateClockOut = datEvent
FROM MyTable.dbo.TimeMTSView
WHERE datEvent < #Today
AND strUniqueID = #nUser
AND blnEventType = 0
AND lngClassificationID = 0
ORDER BY datEvent DESC --This is what fails
SELECT #strLastClock = CONVERT(VARCHAR, #LastDateClockOut, 103);
-- Grab the path to the TimeMTSTrigger.exe written out by the application itself to a log file:
DECLARE #FileContents VARCHAR(MAX)
SELECT #FileContents = BulkColumn
FROM OPENROWSET(BULK'C:\MTSPath\MTSPath.sql', SINGLE_BLOB) x;
SET #cmd = 'Start ' + #FileContents + ' ' + #nUser + ' ' + #blnEventChar + ' ' + #strLastClock
EXEC xp_cmdshell #cmd
I was able to resolve this by not running the SELECT statement on the View I had created in a separate database.
The View had combined two tables from the TIMECLOCKMTS database just to make it easier to grab data. However, the SELECT statement on the View would not run in the SQL Trigger or the exe itself, both would hang and timeout.
By eliminating the View from both the Trigger and EXE and only referencing tables in the Trigger's database this runs perfectly:
USE [TIMECLOCKMTS]
GO
/****** Object: Trigger [dbo].[trgAfterInsert] Script Date: 5/19/2018 2:22:34 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[trgAfterInsert] ON [dbo].[tblTimes]
AFTER INSERT
AS
declare #trg_lngClassificationID int;
SELECT #trg_lngClassificationID=i.lngClassificationID from inserted i;
IF (#trg_lngClassificationID = 0) --If the date Classification is 0 this means it is NORMAL time as opposed to VACATION or HOLIDAY
declare #trg_lngID int;
declare #trg_lngEmployeeID varchar(20);
declare #trg_blnEventType bit;
declare #blnEventChar nvarchar(1);
declare #trg_datEvent datetime;
declare #nUser varchar(255);
declare #cmd varchar(255);
declare #Today varchar(255);
declare #LastDateClockOut datetime;
SELECT #trg_lngID=i.lngID from inserted i;
SELECT #trg_lngEmployeeID=i.lngEmployeeID from inserted i;
SELECT #trg_blnEventType=i.blnEventType from inserted i;
SELECT #trg_datEvent=i.datEvent from inserted i;
SELECT #nUser = strUniqueID FROM dbo.tblEmployees WHERE lngID = #trg_lngEmployeeID AND blnDeleted = 0
SELECT #blnEventChar = CONVERT(NVARCHAR, #trg_blnEventType);
--Grab the path to the TimeMTSTrigger.exe written out by the application itself to a log file:
DECLARE #FileContents VARCHAR(MAX)
SELECT #FileContents=BulkColumn
FROM OPENROWSET(BULK'C:\MTSPath\MTSPath.sql',SINGLE_BLOB) x;
SET #cmd = 'Start ' + #FileContents + ' ' + #nUser + ' ' + #blnEventChar + ' ' + #trg_lngEmployeeID
EXEC xp_cmdshell #cmd

Create snapshot tables on first day of month with date in tablename - stored procedure

I am trying to create an stored procedure in SQL Server that should check current date: if today is the first day of month then create some tables and include yesterday date in the table name. If today is not the first day of month, then do nothing.
The idea qould be to simply call this stored procedure in a daily SSIS package.
I am struggling with the addition of the "else (do nothing" and adding the previous date to the table name.
Would something like this work? I can't get it to compile...
DECLARE #d CHAR(10) = CONVERT(CHAR(8), dateadd(day,-1,GETDATE()), 112)
IF DAY(GETDATE())=1
BEGIN
DECLARE #table1name NVARCHAR(MAX) = CONCATENATE('snapshots_db.Table1_',#d);
select *
into #table1name
from sales_db.Table1;
DECLARE #table2name NVARCHAR(MAX) = CONCATENATE('snapshots_db.Table2_',#d);
select *
into #table2name
from sales_db.Table2;
END
ELSE
BEGIN
1=1
END
your code seems to be with the logic you are trying to achieve. for the 'ELSE nothing' part, you can just literally do nothing, don't use any code in that section. I formatted your query above so you could check each section clearly.
DECLARE #d CHAR(10),
#table1name NVARCHAR(MAX),
#table2name NVARCHAR(MAX);
SET #d = CONVERT(CHAR(8), dateadd(day,-1,GETDATE()), 112);
SET #table1name = CONCATENATE('snapshots_db.Table1_',#d);
SET #table2name = CONCATENATE('snapshots_db.Table2_',#d);
IF DAY(GETDATE())=1
BEGIN
SELECT *
INTO #table1name
FROM sales_db.Table1;
SELECT *
INTO #table2name
FROM sales_db.Table2;
END;

SQL Server - Convert SQL to Stored Procedure

Suppose I have the following structure to a set of tables in my SQL Server (2012) DB:
StartDate: Col1: Col2: .... Coln:
And, the way the DBA set up the database (have no control over that - I only have query access), all the tables with this structure that I'd want to query have, say, names beginning with MyTbl....
So, I would like to create a query that queries ALL these tables at once to get data for a specific StartDate and I've done it using the following SQL:
declare #t table(tablename varchar(50))
declare #sql varchar(max)
set #sql = ''
insert into #t
SELECT t.name AS table_name FROM sys.tables AS t
WHERE t.name LIKE 'MyTbl%'
select #sql = #sql + 'Select ''' + tablename + ''' as Table_Name, t.* From ' + tablename +
' t where StartDate = ''2015-01-01'' +
' union ' from #t
Select #sql = substring(#sql, 1, len(#sql) - 6)
exec(#sql)
In other words:
Find all tables in my DB with names beginning with MyTbl
Query each table for any data with StartDate = '2015-01-01`
Union all those queries together to get one big dataset result
The SQL works perfectly, but I'm getting quite stuck in creating a stored procedure from this query that can take in a parameter for StartDate and I don't know enough about stored procedures to do this correctly.
How could I convert this into a stored procedure that takes a date in for StartDate (to replace the ''2015-01-01'' in the query)?
Any help / guidance would be GREATLY appreciated!!!
THANKS!!!
I noticed you were not looping through each table .. here is something I had put together
CREATE PROCEDURE get_tabledata (#date DATE)
AS
BEGIN
DECLARE #t TABLE (
id INT IDENTITY(1, 1)
,tablename VARCHAR(50)
)
DECLARE #id INT
DECLARE #tablename VARCHAR(max)
DECLARE #sql VARCHAR(max)
SET #sql = ''
INSERT INTO #t
SELECT t.NAME AS table_name
FROM sys.tables AS t
WHERE t.NAME LIKE 'MyTbl%'
SET #id = ##ROWCOUNT
IF (#id > 0)
BEGIN
WHILE (#id > 0)
BEGIN
SET #tablename = (
SELECT tablename
FROM #t
WHERE id = #id
)
SELECT #sql = #sql + 'Select ' + #tablename + ''' as Table_Name, t.* From ' + #tablename + ' t where StartDate = ' + '' + convert(VARCHAR, #date) + ''
SET #sql = #sql + ' union'
Set #id = #id -1;
END
SELECT #sql = substring(#sql, 1, len(#sql) - 6)
END
EXEC (#sql)
END
While it can be a little dense if you're not used to the styling Microsoft uses on these pages, the best place to start would be the Create Procedure documentation on MSDN
https://msdn.microsoft.com/en-us/library/ms187926.aspx
That said, creating a stored procedure is pretty straight forward. Here's a really simple procedure that takes a #startDate parameter and then just returns it back. This is just to illustrate how and where you define your parameters
create procedure dbo.MyProcedure
-- put your input parameters here
#StartDate date
as
--put the body of your procedure (i.e. everything you've written in your OP) here
select #StartDate
go
YOu'll notice however that if you run this twice in a row, you get an error, because it tries to build the same procedure again. Another thing which can come in handy is adding some code before your procedure which will basically check to see if it already exists, and if it does, alter the procedure rather than just blindly re-create it.
This is a snippet from a template I use quite often which handles all of that logic for you. The simplest way to use this is press CTRL-SHIFT-M, which brings up a dialogue to replace all those tags with values you provide.
use [<Database Name, sysname,>]
go
if not exists (select 1
from sys.procedures with(nolock)
where name = '<Procedure Name, sysname,>'
and [schema_id] = schema_id('<Schema, sysname,dbo>')
and type = 'P'
)
exec ('create procedure [<Schema, sysname,dbo>].[<Procedure Name, sysname,>]
as
select ''Procedure not defined.'' as ErrorDescription
return')
--Executed as dynamic SQL since SQL Server Management Studio considures the straight SQL code a syntax error for some reason on the create procedure statement
GO
alter procedure [<Schema, sysname,dbo>].[<Procedure Name, sysname,>]
<Parm 1 Name, sysname,include [#]> <Parm 1 Datatype, sysname,><Parm 1 Default, sql_variant,include [=] if used>,
<Parm 2 Name, sysname,include [#]> <Parm 2 Datatype, sysname,><Parm 2 Default, sql_variant,include [=] if used>
as
/*******************************************************************************************************
********************************************************************************************************/
---------------------------------------------
-- declare variables
---------------------------------------------
---------------------------------------------
-- create temp tables
---------------------------------------------
---------------------------------------------
-- set session variables
---------------------------------------------
set nocount on
---------------------------------------------
-- body of stored procedure
---------------------------------------------
return

Sum up values from databases and parametrize it. [SQL Server]

I want to sum up values from several databases. At this moment I have three databases: SPA_PROD, SPB_PROD and SPC_PROD.
My SQL query:
IF EXISTS (SELECT *
FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[TESTSUM]')
AND TYPE IN (N'P',N'PC'))
DROP PROCEDURE [dbo].[TESTSUM]
GO
CREATE PROC TESTSUM
AS
BEGIN
DECLARE #dbName SYSNAME,
#ObjectSUM INT,
#d datetime
SET #d = '20141113'
DECLARE #SQL NVARCHAR(MAX)
DECLARE #DBObjectStats TABLE (
--DBName SYSNAME,
DBObjects INT)
DECLARE curAllDBs CURSOR FOR
SELECT name
FROM MASTER.dbo.sysdatabases
WHERE name like '%PROD'
ORDER BY name
OPEN curAllDBs
FETCH curAllDBs INTO #dbName
WHILE (##FETCH_STATUS = 0) -- db loop
BEGIN
--SQL QUERY
SET #SQL = 'select #dbObjects = sum(doctotal) from ' +
QuoteName(#dbName) + '..Invoice
where DocDate = ''' + cast(#d as varchar(25)) + ''''
PRINT #SQL -- Debugging
EXEC sp_executesql #SQL, N'#dbObjects int output',
#dbObjects = #ObjectSUM output
INSERT #DBObjectStats
SELECT #ObjecSUM
FETCH curAllDBs INTO #dbName
END
CLOSE curAllDBs
DEALLOCATE curAllDBs
-- Return results
SELECT sum(DBObjects) [InvoiceSUM] FROM #DBObjectStats
END
GO
-- Execute stored procedure
EXEC TESTSUM
GO
And this work perfect and giving me right sum from all my DBs: 120 000$ ( 25 000 from SPA_PROD , 95 000 SPC_PROD and 0 (NULL) from SPB_PROD.
What I want to do:
I would like to parametrize, which allows me to choose date and databases. For example I want to choose SPA_PROD and SPB_PROD with date 2014-01-01 in another case I want all databases (SPA + SPB + SPC with another date.
Is this even possible? Any ideas?
I can use everything what gives me SQL Server 2012 and T-SQL. Maybe this technology offers me easiest way to do this.
I am also using SAP Crystal Reports to convert SQL output into a beautiful report.
Sorry for my English and I tried to describe to you my problem as far as I could. If you want any additional information which helps u to help me -> ask me :).
You can create a User-Defined Table Type:
CREATE TYPE DBTable AS TABLE
(
DBName VARCHAR(128)
);
You can use it as an input parameter of your stored procedure. As well as the date parameter.
CREATE PROCEDURE TESTSUM
#Databases DBTable READONLY
,#Date DATETIME
AS
BEGIN
...
...
...
You call it like this:
DECLARE #T AS DBTable;
DECLARE #D AS DATETIME = GETDATE();
INSERT INTO #T VALUES ('DB1', 'DB2', 'DB3')
EXEC TESTSUM #T, #D
maybe instead of
SELECT name
FROM MASTER.dbo.sysdatabases
use
SELECT name
FROM #temptable
and insert into #temptable specific db you want
Using your example I modified it to accept a string of database names (generated through you crystal reports select action). Then passing this string with the date in question to first validate the database exist and if online add the required union clause to the generated SQL code.
CREATE PROCEDURE TESTSUM
#DbNameS NVARCHAR(max)
,#Date DATETIME
AS
BEGIN
DECLARE #SQL NVARCHAR(MAX) = ''
/* ADD EXTRA ',' RO STRING ARRAY OF DATABASES */
SET #DbNameS = #DbNameS + ',';
DECLARE #L INT = LEN(#DbNameS);
DECLARE #D INT = 0;
DECLARE #LD INT = 1;
DECLARE #DBF VARCHAR(50);
DECLARE #ACTIVE INT = 0;
/* START SQL QUERY */
SET #SQL = 'SELECT SUM([InvoiceSUM]) AS [InvoiceSUM] FROM ( SELECT '''' AS DB, 0.00 AS [InvoiceSUM]' + CHAR(13)
/* LOOP THROUGH EACH DBF NAME PASSED CHECKING IF VALID AND ONLINE */
WHILE #D < #L
BEGIN
SET #D = CHARINDEX(',', #DbNameS,#LD);
IF #LD != #D
BEGIN
SET #DBF = SUBSTRING(#DbNameS,#LD,#D-#LD)
/* VALIDATE DBF IS VALID AND ACTIVE */
SELECT #ACTIVE = COUNT(*) FROM SYS.databases WHERE name = #DBF AND [state] = 0
IF #ACTIVE = 1
BEGIN
/*
BEGIN CODE TO UNION THE SUM RESULTS FOR EACH ACTIVE AND VALID DBF
TO MAKE IT WORK WITH SOME EXISTING DBF's ON MY SYSTEM I CHANGED THE SUMMARY CODE FOR TESTING
*/
SET #SQL = #SQL + 'UNION SELECT '''+ #DBF +''' AS DB, ISNULL(SUM( CAST(DVE AS DECIMAL(18,10)) ),0) AS [InvoiceSUM] FROM '+ #DBF + '.DBO.SO_MSTR WHERE CAST(RecordCreated AS DATE) = '''+ CAST(#Date AS VARCHAR(20)) + '''' + CHAR(13)
END;
END;
SET #LD = #D + 1;
END;
/* CLOSE OUT UNION SUMMARY QUERY */
SET #SQL = #SQL + ') AS DATA'
/* OUTPUT RESULTS */
EXEC SP_EXECUTESQL #SQL
END;
Crystal reports would effective be generating this code: EXEC TESTSUM 'SPA_PROD,SPB_PROD,SPC_PROD','12/09/2014'

SQL Select maxDate in SP

USE [MyDatabase]
GO
Object: StoredProcedure [dbo].[SP_MyProcedure]
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[SP_MyProcedure]
-- Add the parameters for the stored procedure here
#StartDate NVARCHAR(19),
#EndDate NVARCHAR(19)
AS
BEGIN
SET NOCOUNT ON;
Insert statements for procedure here
DECLARE #FirstQuery nvarchar(1500);
DECLARE #SecondQuery nvarchar(1500);
DECLARE #TSQL nvarchar(4000);
SET #FirstQuery =
'
SELECT * FROM OPENQUERY(LinkedServer,
''
SELECT * FROM Server.Table
WHERE Name IN
(SELECT Tagname COLLATE DATABASE_DEFAULT FROM LocalServer.MyServer.dbo."NameList"
WHERE LOCATION = ''''X'''' AND SOURCE = ''''Y'''') AND TIMESTAMP >= ''''' + #StartDate + ''''' AND TIMESTAMP < ''''' + #EndDate + ''''''')';
My question is: How could I include max(date) in the code, so the #startDate would be compared to the latest available date for a column in the DB instead of todays date.
The code you see works fine, however when I choose a later date than the last existing date in the DB the query runs for a while before it returns an error that no columns were found.
Thanks in advance for any suggetions.
HELP.
The following part of the code doesn't work, but I can't figure out why. Does anyone have any suggestions? Thank you
-- Insert statements for procedure here
DECLARE #FirstQuery nvarchar(1500);
DECLARE #SecondQuery nvarchar(1500);
DECLARE #TSQL nvarchar(4000);
DECLARE #MaxTimeStamp nvarchar(19);
SET#MaxTimeStamp =
'SELECT MAX(TimeStamp) From OPENQUERY(LinkedServer)'
IF #StartDate <= #MaxTimeStamp
BEGIN
SET #FirstQuery =
'
SELECT * FROM OPENQUERY(LinkedServer,
''
SELECT * FROM Server.Table
WHERE Name IN
(SELECT Tagname COLLATE DATABASE_DEFAULT FROM LocalServer.MyServer.dbo."NameList"
WHERE LOCATION = ''''X'''' AND SOURCE = ''''Y'''') AND TIMESTAMP >= ''''' + #StartDate + ''''' AND TIMESTAMP < ''''' + #EndDate + ''''''')';
END
Well, you could replace it with a subquery:
(select max(timestamp) from NameList)
If this is a stored procedure, you might want to put this in a variable, something like:
declare #MaxTimestamp datetime;
select #MaxTimestamp = max(TimeStamp) from NameList
-- in your query, something like
coalesce(#StartTime, maxTimeStamp)
If performance is an issue, try adding an index on NameList(Location, Source, Timestamp).
Try this...
DECLARE #maxDate datetime;
SELECT #maxDate = MAX(timestamp) FROM OPENQUERY(...);
IF #StartDate <= #maxDate
BEGIN
--Your original query here
END