SQL Server Date Check based on input - sql-server

This problem has bugged me so many times and i have now decided to try and find the proper solution for it, instead of my long-winded/dirty/horrible way I always revert to (copying the sql statement)
The table has a date column with a default value of NULL
Now what i need to do is pass a value (-1,0,1) to an sql statement which determines what to pull back
-1 - should bring back all rows
0 - should bring back rows with a NULL date value
1 - should bring back rows with a valid date value
I have tried using CASE statements, but the logical operator would need to change depending on the value being passed to the query.
Lets just call the table Quotes and the column completed, so...
CREATE TABLE 'Quotes'
(
completed DATETIME default(NULL)
)
Solution needs to be for SQL Server 2000, and i'm working in stored procedures not dynamic SQL. So it really needs to be all in one statement.
Thanks
Dan

Something like this in the WHERE clause
WHERE (#param = -1)
OR (#param = 0 AND completed IS NULL)
OR (#param = 1 AND completed IS NOT NULL)

Try this:
declare #param int
set #param=-1
declare #sql varchar(2000)
set #sql='select * from quotes '+
case #param when 0 then 'where completed is null'
when 1 then 'where completed is not null'
when -1 then ''
end
exec(#sql)
Raj

Related

Trying to use variable as where clause

I have a situation where I might have a date to search on and I might not. I have a variable that creates a where clause: where field1 = date, if there is a date otherwise i create a blank varable. the problem is adding this to the end of my sql statement.
Select * from table + #where
Select # from table & #where
neither work
msg 102, Level 15, State 1, Line 65
Incorrect syntax near '+'
The best practice would be a procedure with code that allows having a NULL argument. Dynamic SQL can get injected if done poorly or become hard to maintain if you have conditional branches to add more joins, clauses in the WHERE, etc.
CREATE PROCEDURE your_proc
#search_date DATETIME
AS
BEGIN
SELECT *
FROM your_table
WHERE your_date_col >= ISNULL(#search_date, '9999-12-31')
END
GO
Now, if you have a variable, you can call your procedure with it:
DECLARE #variable DATETIME = '2018-01-01'
EXEC your_proc #variable
Or, you can leave it NULL and run the same code:
EXEC your_proc NULL
you can alter the where clause so it will only use the variable when it is not empty,
like this for example
declare #SearchValue date = null -- set a date here if you want to use this variable in the where clause
select t.*
from YourTable t
where (#SearchValue is null or t.YourDateColumn = #SearchValue)
when the variable #SearchValue is empty, then there will be no check done because the expression between the brackets will already be true
Like this you can avoid using dynamic sql

Replace Getdate() with stored procedure

We have a job with couple of steps and almost all of the steps use getdate(), but instead we want to get the date from a specific table and column. The table includes only two columns status as ready (doesn't change) and statusdate (dynamic). The plan is to create a stored procedure and replace the getdate() with that stored procedure.
How do I write the stored procedure? How do I declare a variable?
CREATE PROCEDURE SP_DATE
#StatusDate DATETIME
AS
BEGIN
SELECT StatusDate
FROM [DB_Name].[dbo].[Table_Name]
WHERE status = ready
END
Thank you!
Your jobs use getdate() function therefore in order to replace it with custom programmatic object you should use function as well and not a stored procedure. With a function like this
CREATE FUNCTION dbo.StatusDate ()
RETURNS DATETIME
AS
BEGIN
RETURN (SELECT
StatusDate
FROM Table_Name
WHERE status = 'ready')
END
you can replace getdate directly
SELECT
id
FROM Your_Job_List yjl
WHERE yjl.aDate < dbo.StatusDate()--getdate()
yet there are some questions to the design. One biggest single task of RDBMS is joining tables and perhaps a query similar to next one might be better
SELECT
id
FROM Your_Job_List yjl
,Table_Name tn
WHERE yjl.aDate < tn.StatusDate
AND tn.status = 'ready'
CREATE PROCEDURE spRunNextDate
AS
BEGIN
--SET NOCOUNT ON
declare #runDate datetime
select #runDate = MIN(StatusDate)
from [DB_Name].[dbo].[Table_Name]
where [status] = 'ready'
IF (#runDate IS NULL)
BEGIN
PRINT 'NO Dates in Ready State.'
RETURN 0
END
PRINT 'Will Run Date of ' + CAST(#runDate as varchar(20))
-- Notice the MIN or MAX usage above to get only one date per "run"
END
GO
There are huge holes and questions raised in my presumptuous sp above, but it might get you to thinking about why your question implies that there is no parameter. You are going to need a way to mark the day "done" or else it will be run over and over.

Sql server query with lots of substrings performance

I have stored procedure in which i perform a bulk insert into a temp table and perform substring on its field to get the different row required for the main table.
The no. of columns for the main table is 66 and the row added after each run of the sp is approx 5500.
Code for bulk insert part:
CREATE TABLE [dbo].[#TestData] (
logdate DATETIME,
id CHAR(15),
value VARCHAR(max)
)
BEGIN TRANSACTION
DECLARE #sql VARCHAR(max)
SET #sql = 'BULK INSERT [dbo].[#TestData] FROM ''' + #pfile + ''' WITH (
firstrow = 2,
fieldterminator = ''\t'',
rowterminator = ''\n''
)'
EXEC(#sql)
IF (##ERROR <> 0)
BEGIN
ROLLBACK TRANSACTION
RETURN 1
END
COMMIT TRANSACTION
Code for substring part :
CASE
WHEN (PATINDEX('%status="%', value) > 0)
THEN (nullif(SUBSTRING(value, (PATINDEX('%status="%', value) + 8), (CHARINDEX('"', value, (PATINDEX('%status="%', value) + 8)) - (PATINDEX('%status="%', value) + 8))), ''))
ELSE NULL
END,
This substring code is used in insert into and is similar for all the 66 columns.
It takes around 20-25 sec for the sp to execute. I have tried indexing on temp table,droped foreign keys,droped all indexes,droped primary key but still it takes the same time.
So my question is can the performance be improved?
Edit: The application for interface is visual foxpro 6.0.
As sql server is slow with string manipulation and doing all the string manipulations on foxpro now. New to foxpro Any suggestions how to send null from foxpro to sqlserver?
Never worked with null in foxpro 6.0.
Since you are not really leveraging the features of PATINDEX() here you may want to examine the use of CHARINDEX() instead, which, despite its name, operates on strings and not only on characters. CHARINDEX() may prove to be faster than PATINDEX() since it is a somewhat simpler function.
Indexes won't help you with those string operations because you're not searching for prefixes of strings.
You should definitely look into options to avoid the excessive use of PATINDEX() or CHARINDEX() inside the statement; there are up to 4(!) invocations thereof in your CASE for each of your 66 columns in every record processed.
For this you may want to split the string operations into multiple statements to pre-compute the values for the start and end index of the substring of interest, like
UPDATE temptable
SET value_start_index = CHARINDEX('status="', value) + 8
UPDATE temptable
SET value_end_index = CHARINDEX('"', value, value_start_index)
WHERE value_start_index >= 8
UPDATE temptable
SET value_str = SUBSTRING(value, value_start_index, value_end_index - value_start_index)
WHERE value_end_index IS NOT NULL
SQL Server is rather slow in dealing with strings. For this number of executions it would be best to use SQL CLR user defined function. There is not much more you can do beyond that.

TimeStamp in sql

I have a table named Table1 which contains an ID and TimeStamp.
Table structure
ID TimeStamp
1 0x0000000000047509
But when I compare the combination of these fields, it always shows false. What is the reason for this?
My Query is :
DECLARE #ID int
DECLARE #TimeStamp timestamp
SET #ID = 1
SET #TimeStamp = 0x0000000000047509
If EXISTS(SELECT 1 FROM Table1 WHERE ID = #ID AND TimeStamP = #TimeStamp)
BEGIN
SELECT 1 AS RetVal
END
ELSE
BEGIN
SELECT -1 AS RetVal
END
This will simply work in a Query analyzer.But this will not work in a stored Procedure. Any solution for this?
If I recall, TimeStamp in SQL server is not actually a time stamp but is intended for mostly internal use (I used to use TimeStamps for last updated type fields in MySQL but thats not the right type in SQL Server it seems). I believe you will just want to use a datetime or datetime2 data type depending on the accuracy you want.
Here is a link to the choices available: http://msdn.microsoft.com/en-us/library/ms186724.aspx
It should work, I have tried it myself and it worked fine. I ran the following from an SP and it output OK.
DECLARE #t timestamp
SET #t = 0x00000000000055F5
IF EXISTS(SELECT 1 FROM dbo.[User] WHERE [Timestamp]=#t AND UserId=10)
BEGIN
PRINT 'OK'
END
Maybe your timestamp has changed before the SP runs. The timestamp changes every time you insert or update the row. Really rowversion is a better name for it IMHO.
Good resrouces: timestamp and Timestamps vs Datetime data types

Getting info about running update query

On Sql Server 2008, I have a slow-running update query that has been working for 3 hours. Is there any way to get any statistics (say, how many rows have changed so far or something else) about this execution (of course, while executing) ? So that I can make a decision between canceling query and optimize it or let it finish.
Or else, should I have done something before execution ? If so, what can be done before execution for getting info about an update query while it is running. (of course, without affecting the performance largely)
an update for clarifying:
the update statement mentioned in question is (e.g.):
UPDATE myTable SET col_A = 'value1' WHERE col_B = 'value2'
In other words, it is a single query that updates whole table
What can you do now?
You could try running a separate query using the WITH(NOLOCK) table hint to see how many rows have been updated.
e.g. if the update statement is:
UPDATE MyTable
SET MyField = 'UPDATEDVALUE'
WHERE ID BETWEEN 1 AND 10000000
You could run this:
SELECT COUNT(*)
FROM MyTable WITH (NOLOCK)
WHERE ID BETWEEN 1 AND 10000000
AND MyField = 'UPDATEDVALUE'
What can you do in future?
You could do the update in batches, and output progress as it goes. e.g. use a loop to update the records in chunks of say 1000 (arbitary value for point of explanation). After each update chunk completes, print out the progress (assuming you are running it from SSMS) e.g.
DECLARE #RowCount INTEGER
SET #RowCount = 1
DECLARE #Message VARCHAR(500)
DECLARE #TotalRowsUpdated INTEGER
SET #TotalRowsUpdated = 0
WHILE (#RowCount > 0)
BEGIN
UPDATE TOP (1000) MyTable
SET MyField = 'UPDATEDVALUE'
WHERE ID BETWEEN 1 AND 10000000
AND MyField <> 'UPDATEDVALUE'
SELECT #RowCount = ##ROWCOUNT
SET #TotalRowsUpdated = #TotalRowsUpdated + #RowCount
SELECT #Message = CAST(GETDATE() AS VARCHAR) + ' : ' + CAST(#TotalRowsUpdated AS VARCHAR) + ' records updated in total'
RAISERROR (#Message, 0, 1) WITH NOWAIT
END
Using RAISERROR like this ensures progress messages are printed out immediately. If you used PRINT instead, the messages are spooled up and not output immediately so you wouldn't get to see real-time progress.
From http://www.sqlnewsgroups.net/group/microsoft.public.sqlserver.server/topic19776.aspx
"You should be able to do a dirty read to produce a rowcount of rows
satisfying the update clause (assuming that value wasn't already used)."
If you set your query to use "READ UNCOMMITTED" you should be able to see rows that have been updated by your statement with a select statement having the proper criteria. I'm no expert on this...so just my guess.
It would really help us if you posted the query so that we can look at it and try to help you.
One way to see what is going on is to use SQL Profiler to profile the updates / inserts.
Without knowning exactly what you're doing I can't say for sure, but if you put the update in a procedure then use the PRINT command to output status updates at specific steps, these messages are output during the procedures run time in the messages tab that's next to the results tab.

Resources