Is there a combination of “LIKE” and “IN” in SQL?

后端 未结 25 1507
灰色年华
灰色年华 2020-11-22 03:08

In SQL I (sadly) often have to use \"LIKE\" conditions due to databases that violate nearly every rule of normalization. I can\'t change that right now. But tha

相关标签:
25条回答
  • 2020-11-22 03:36

    I know this is very late, but I had a similar situation. I needed a "Like In" operator for a set of stored procedures I have, which accept many parameters and then uses those parameters to aggregate data from multiple RDBMS systems, thus no RDBMS-specific tricks would work, however the stored procedure and any functions will run on MS SQL Server, so we can use T-SQL for the functionality of generating the full SQL statements for each RDBMS, but the output needs to be fairly RDBMS-independent.

    This is what I've come up with for the moment to turn a delimited string (such as a parameter coming into a stored procedure) into a block of SQL. I call it "Lichen" for "LIKE IN". Get it?

    Lichen.sql

    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    -- =======================================================================
    -- Lichen - Scalar Valued Function
    -- Returns nvarchar(512) of "LIKE IN" results.  See further documentation.
    -- CREATOR: Norman David Cooke
    -- CREATED: 2020-02-05
    -- UPDATED:
    -- =======================================================================
    CREATE OR ALTER FUNCTION Lichen 
    (
        -- Add the parameters for the function here
        @leadingAnd bit = 1,
        @delimiter nchar(1) = ';',
        @colIdentifier nvarchar(64),
        @argString nvarchar(256)
    )
    RETURNS nvarchar(512)
    AS
    BEGIN
        -- Declare the return variable here
        DECLARE @result nvarchar(512)
    
        -- set delimiter to detect (add more here to detect a delimiter if one isn't provided)
        DECLARE @delimit nchar(1) = ';'
        IF NOT @delimiter = @delimit 
            SET @delimit = @delimiter
    
    
        -- check to see if we have any delimiters in the input pattern
        IF CHARINDEX(@delimit, @argString) > 1  -- check for the like in delimiter
        BEGIN  -- begin 'like in' branch having found a delimiter
            -- set up a table variable and string_split the provided pattern into it.
            DECLARE @lichenTable TABLE ([id] [int] IDENTITY(1,1) NOT NULL, line NVARCHAR(32))
            INSERT INTO @lichenTable SELECT * FROM STRING_SPLIT(@argString, ';')
    
            -- setup loop iterators and determine how many rows were inserted into lichen table
            DECLARE @loopCount int = 1
            DECLARE @lineCount int 
            SELECT @lineCount = COUNT(*) from @lichenTable
    
            -- select the temp table (to see whats inside for debug)
            --select * from @lichenTable
    
            -- BEGIN AND wrapper block for 'LIKE IN' if bit is set
            IF @leadingAnd = 1
                SET @result = ' AND ('
            ELSE
                SET @result = ' ('
    
            -- loop through temp table to build multiple "LIKE 'x' OR" blocks inside the outer AND wrapper block
            WHILE ((@loopCount IS NOT NULL) AND (@loopCount <= @lineCount))
            BEGIN -- begin loop through @lichenTable
                IF (@loopcount = 1) -- the first loop does not get the OR in front
                    SELECT @result = CONCAT(@result, ' ', @colIdentifier, ' LIKE ''', line, '''') FROM @lichenTable WHERE id = @loopCount
                ELSE  -- but all subsequent loops do
                    SELECT @result = CONCAT(@result, ' OR ', @colIdentifier, ' LIKE ''', line, '''') FROM @lichenTable WHERE id = @loopCount
                SET @loopcount = @loopCount + 1     -- increment loop
            END -- end loop through @lichenTable
    
            -- set final parens after lichenTable loop
            SET @result = CONCAT(@result, ' )')
        END  -- end 'like in' branch having found a delimiter
        ELSE -- no delimiter was provided
        BEGIN   -- begin "no delimiter found" branch
            IF @leadingAnd = 1 
                SET @result = CONCAT(' AND ', @colIdentifier, ' LIKE ''' + @argString + '''')
            ELSE
                SET @result = CONCAT(' ', @colIdentifier, ' LIKE ''' + @argString + '''')
        END     -- end "no delimiter found" branch
    
        -- Return the result of the function
        RETURN @result
    END  -- end lichen function
    
    GO
    

    The delimiter detection is possibly planned, but for now it defaults to a semicolon so you can just put default in there. There are probably bugs in this. The @leadingAnd parameter is just a bit value to determine if you want a leading "AND" put in front of the block so it fits in nicely with other WHERE clause additions.

    Example Usage (with delimiter in argString)

    SELECT [dbo].[Lichen] (
       default        -- @leadingAND, bit, default: 1
      ,default        -- @delimiter, nchar(1), default: ';'
      ,'foo.bar'      -- @colIdentifier, nvarchar(64), this is the column identifier
      ,'01%;02%;%03%' -- @argString, nvarchar(256), this is the input string to parse "LIKE IN" from
    )
    GO
    

    Will return a nvarchar(512) containing:

     AND ( foo.bar LIKE '01%' OR foo.bar LIKE '02%' OR foo.bar LIKE '%03%' ) 
    

    It will also skip the block if the input does not contain a delimiter:

    Example Usage (without delimiter in argString)

    SELECT [dbo].[Lichen] (
       default        -- @leadingAND, bit, default: 1
      ,default        -- @delimiter, nchar(1), default: ';'
      ,'foo.bar'      -- @colIdentifier, nvarchar(64), this is the column identifier
      ,'01%'          -- @argString, nvarchar(256), this is the input string to parse "LIKE IN" from
    )
    GO
    

    Will return a nvarchar(512) containing:

     AND foo.bar LIKE '01%'
    

    I'm going to continue work on this, so if I've overlooked something (glaringly obvious or otherwise) please feel free to comment or reach out.

    0 讨论(0)
  • 2020-11-22 03:37

    I may have a solution for this, although it will only work in SQL Server 2008 as far as I know. I discovered that you can use the row-constructor described in https://stackoverflow.com/a/7285095/894974 to join a 'fictional' table using a like clause. It sounds more complex then it is, look:

    SELECT [name]
      ,[userID]
      ,[name]
      ,[town]
      ,[email]
    FROM usr
    join (values ('hotmail'),('gmail'),('live')) as myTable(myColumn) on email like '%'+myTable.myColumn+'%' 
    

    This will result in all users with an e-mail adres like the ones provided in the list. Hope it's of use to anyone. The problem had been bothering me a while.

    0 讨论(0)
  • 2020-11-22 03:38

    One approach would be to store the conditions in a temp table (or table variable in SQL Server) and join to that like this:

    SELECT t.SomeField
    FROM YourTable t
       JOIN #TempTableWithConditions c ON t.something LIKE c.ConditionValue
    
    0 讨论(0)
  • 2020-11-22 03:38

    In Oracle you can use a collection in the following way:

    WHERE EXISTS (SELECT 1
                    FROM TABLE(ku$_vcnt('bla%', '%foo%', 'batz%'))
                   WHERE something LIKE column_value)
    

    Here I have used a predefined collection type ku$_vcnt, but you can declare your own one like this:

    CREATE TYPE my_collection AS TABLE OF VARCHAR2(4000);
    
    0 讨论(0)
  • 2020-11-22 03:39

    With PostgreSQL there is the ANY or ALL form:

    WHERE col LIKE ANY( subselect )
    

    or

    WHERE col LIKE ALL( subselect )
    

    where the subselect returns exactly one column of data.

    0 讨论(0)
  • 2020-11-22 03:39

    If you are using MySQL the closest you can get is full-text search:

    Full-Text Search, MySQL Documentation

    0 讨论(0)
提交回复
热议问题