25

Can I replace the = statement with the LIKE one for the integers ?

by eg. are the following the same thing:

select * from FOOS where FOOID like 2    
// and 
select * from FOOS where FOOID = 2

I'd prefer to use LIKE instead of = because I could use % when I have no filter for FOOID...

SQL Server 2005.

EDIT 1 @Martin
enter image description here

Martin Smith
  • 438,706
  • 87
  • 741
  • 845
serhio
  • 28,010
  • 62
  • 221
  • 374
  • 3
    What do you expect as result for `LIKE 2` ?? An INT value either matches exactly, or not - there's no **LIKE** functionality for INTs, really.... – marc_s Apr 22 '11 at 11:27
  • @marc_s: it will be exactly the same as for the strings with no wildcards: an exact match. The @op is trying to reinvent a solution for filtering on "this or anything": http://stackoverflow.com/questions/5754619/sql-where-myid-any – Quassnoi Apr 22 '11 at 11:30
  • If you do go this route, be sure to explicitly cast FOOID so the behavior is clear to someone else reviewing your code. – Mayo Apr 22 '11 at 11:31
  • Your plan execution %'s aren't accurate in this case because you have the INSERT statement in there and it's skewing the results. The INSERT statement is much more expensive than either of the SELECTs. Just check the execution plan for the two SELECT statments by themselves. – rsbarro Apr 22 '11 at 12:02
  • @Martin Sorry, I was addressing @serhio. He posted an image in the question that shows the performance percentage as the same for both selects. – rsbarro Apr 22 '11 at 12:13
  • @serhio - As @rsbarro says above you need to exclude the `CREATE TABLE` from the batch as this will dominate the cost. You can also use `SET STATISTICS IO ON` to see the actual logical reads per statement. @rsbarro - Yes. I confused myself I clicked through on the comment notification and thought it was a comment on my answer! – Martin Smith Apr 22 '11 at 12:14
  • Also the plan indicates that when it was generated it has no statistics yet for the `#temp` table as it has not yet been created so it assumes that it will contain only one row. This is why there is no difference between the 2 estimated costs. Use the Actual Plan not the Estimated Plan. – Martin Smith Apr 22 '11 at 12:26
  • Your question has at least `5` votes for the [tag:like-operator] tag. Could I kindly request that you suggest [tag:sql-like] as a [synonym](http://stackoverflow.com/tags/like-operator/synonyms)? – Kermit Apr 02 '13 at 18:39

5 Answers5

22
select * from FOOS where FOOID like 2

should be avoided as it will cause both sides to be implicitly cast as varchar and mean that an index cannot be used to satisfy the query.

CREATE  TABLE #FOOS
(
FOOID INT PRIMARY KEY,
Filler CHAR(1000)
)
INSERT INTO #FOOS(FOOID)
SELECT DISTINCT number 
FROM master..spt_values


SELECT * FROM #FOOS WHERE FOOID LIKE 2

SELECT * FROM #FOOS WHERE FOOID = 2

DROP TABLE #FOOS

Plans (notice the estimated costs)

enter image description here

Another way of seeing the difference in costs is to add SET STATISTICS IO ON

You see that the first version returns something like

Table '#FOOS__000000000015'. Scan count 1, logical reads 310

The second version returns

Table '#FOOS__000000000015'. Scan count 0, logical reads 2

This is beacuse the reads required for the seek on this index are proportional to the index depth whereas the reads required for the scan are proportional to the number of pages in the index. The bigger the table gets the larger the discrepancy between these 2 numbers will become. You can see both of these figures by running the following.

SELECT index_depth, page_count
FROM
sys.dm_db_index_physical_stats (2,object_id('tempdb..#FOOS'), DEFAULT,DEFAULT, DEFAULT)
WHERE object_id = object_id('tempdb..#FOOS') /*In case it hasn't been created yet*/
Martin Smith
  • 438,706
  • 87
  • 741
  • 845
  • 1
    in other words, you don't recommend to use `like` instead of `=` – serhio Apr 22 '11 at 11:32
  • 4
    @serhio - definitely not because instead of just doing a straight forward index seek on the `id` column it will need to scan all the rows. – Martin Smith Apr 22 '11 at 11:35
  • @serhio - That looks like you are running the "Estimated" execution plan. Try the "Actual" execution plan. When the estimated plan is generated the temp table does not exist yet so there are no statistics and SQL Server will assume the table contains only 1 row. Probably better to just create the `#temp` table on its own then run the 2 statements. – Martin Smith Apr 22 '11 at 12:24
  • @serhio - I haven't got SQL Server 2005 on this machine to play with so am not sure if there's some bizarre issue with its estimated costs or some issue with the way you are generating the plans (did you populate the table first?) but can assure you that a seek can be massively more efficient than a scan. I'll edit my answer to show another way this can be demonstrated. – Martin Smith Apr 22 '11 at 12:40
  • topic picture updated with the reinstalled English version of SQL Studio :) + Actual Path ) – serhio Apr 22 '11 at 16:00
5

Use a CASE statement to convert an input string to an integer. Convert the wildcard % to a NULL. This will give better performance than implicitly converting the entire int column to string.

CREATE PROCEDURE GetFoos(@fooIdOrWildcard varchar(100))
AS
BEGIN
    DECLARE @fooId int

    SET @fooId =
        CASE
            -- Case 1 - Wildcard 
            WHEN @fooIdOrWildcard = '%'
                THEN NULL
            -- Case 2 - Integer
            WHEN LEN(@fooIdOrWildcard) BETWEEN 1 AND 9
            AND @fooIdOrWildcard NOT LIKE '%[^0-9]%'
                THEN CAST(@fooIdOrWildcard AS int)
            -- Case 3 - Invalid input
            ELSE 0
        END

    SELECT FooId, Name
    FROM dbo.Foos
    WHERE FooId BETWEEN COALESCE(@fooId, 1) AND COALESCE(@fooId, 2147483647)
END
Anthony Faull
  • 17,549
  • 5
  • 55
  • 73
3

Yes, you can just use it:

SELECT  *
FROM    FOOS
WHERE   FOOID like 2   

or

SELECT  *
FROM    FOOS
WHERE   FOOID like '%'  

Integers will be implicitly converted into strings.

Note that neither of these condition is sargable, i. e. able to use an index on fooid. This will always result in a full table scan (or a full index scan on fooid).

Quassnoi
  • 413,100
  • 91
  • 616
  • 614
  • in other words, you agree that the SQLs in example I gave are equivalent – serhio Apr 22 '11 at 11:38
  • @serhio: logically, yes, they are equivalent. The execution plans for them will differ, however: if `fooid` is an integer, the first one will never be able to use an index on `fooid` and will always have to process all records in the table, while the second one may use the index. – Quassnoi Apr 22 '11 at 11:40
2

This is a late comment but I thought maybe some other people are looking for the same thing so as I was able to find a solution for this, I thought I should share it here:)

A short description of the problem: the problem I had was to be able to use the wild card foe integer data types. I am using SQL Server and so my syntax is for SQL Server. I have a column which shows department number and I wanted to pass a variable from my page from a drop down menu. There is an 'All' option as well which in that case I wanted to pass '%' as the parameter. I was using this:

select * from table1 where deptNo Like @DepartmentID

It was working for when I pass a number but not for % because sql server implicitly converts the @DepartmentID to int (as my deptNo is of type int)

So I casted the deptNo and that fixed the issue:

select * from table1 where CAST(deptNo AS varchar(2)) Like @DepartmentID

This one works for both when I pass a number like 4 and when I pass %.

j0k
  • 22,600
  • 28
  • 79
  • 90
Amir Tofighi
  • 287
  • 2
  • 4
  • Just worth keeping in mind that applying a function to a column like that will very likely make your query non-[sargable](http://en.wikipedia.org/wiki/Sargable "Sargable (Wikipedia)"). – Andriy M Aug 16 '12 at 05:37
  • This was the only solution I could figure out. Is there a better solution which allows you to use a sargable query? – Amir Tofighi Sep 21 '12 at 16:43
  • Not sure. Maybe it's not possible to come up with a sargable query in this case since type conversion, whether explicit or implicit, would inevitably be involved. – Andriy M Sep 21 '12 at 17:52
1

Use NULL as the parameter value instead of % for your wildcard condition

select * from table1 where (@DepartmentID IS NULL OR deptNo = @DepartmentID)
BethS
  • 31
  • 6