Вопрос

I'm attempting to use an alpha between in SQL; however, I want it to be based on the beginnings of the words only. I am using T-SQL on Microsoft SQL Server 2005.

My current SQL is:

SELECT * FROM table WHERE LEFT(word,1) BETWEEN 'a' AND 't

However, this only works for first letter. I'd like to expand this to work for any beginnings of words. For instance, between 'a' and 'ter'. Now, I am building this dynamically, so I could do:

SELECT * FROM table WHERE LEFT(word,1) >= 'a' AND LEFT(word,3) <= 'ter'

However, I'd like to know if there is a simpler way in SQL to make a dynamic beginning-of-word between.

EDIT:::

Follow up question, words less than the length of the checked value should be considered less than in the between. For instance, me is less than mem so word < 'mem' should include me.

EDIT::: Attempting using padding, as suggested. The below does work; however, the added 'a's can cause issue. For instance, if we want words between 'a' and 'mera' and the word being checked is 'mer', this will be included because the left trim of 'mer' becomes 'mera' with added characters. I would like a solution that does not include this issue.

DECLARE @lb varchar(50)
DECLARE @ub varchar(50)

SET @lb='ly'
SET @ub='z'

SELECT name
FROM table
WHERE
LEFT(
    CASE
        WHEN LEN(name) < LEN(@lb) THEN name+REPLICATE('a',LEN(@lb)-LEN(name))
        ELSE name
    END,
    LEN(@lb)
) >= @lb
AND
LEFT(CASE
        WHEN LEN(name) < LEN(@ub) THEN name+REPLICATE('a',LEN(@ub)-LEN(name))
        ELSE name
    END,
    LEN(@ub)
) <= @ub

EDIT::: Attempted solution, although CASE heavy. Mack's solution is better, though this works as well. LEFT('andy', 200000) will return 'andy', not an error as an OO language would, behavior I did not expect.

DECLARE @lb varchar(50)
DECLARE @ub varchar(50)

SET @lb='a'
SET @ub='lyar'

SELECT *
FROM testtable
WHERE
CASE
    WHEN LEN(word) < LEN(@lb) THEN 0
    WHEN LEFT(word, LEN(@lb)) >= @lb THEN 1
    ELSE 0
END = 1
AND
CASE
    WHEN LEN(word) < LEN(@ub) THEN
        CASE
            WHEN LEFT(@ub,LEN(word)) = word THEN 1
            ELSE 0
        END
    WHEN LEFT(word, LEN(@ub)) <= @ub THEN 1
    ELSE 0
END = 1

Thanks in advance!

Это было полезно?

Решение 2

You need to use the STUFF function to achieve what you are looking for explicitly. If you follow the link says it deletes a specified number of characters at the end of the string and replaces them with another string. Combine the with the LEN function and we can get you on the road.

--Test Data
DECLARE @table AS TABLE (word char(10))

INSERT INTO @table VALUES ('me')
INSERT INTO @table VALUES ('mem')
INSERT INTO @table VALUES ('tap')
INSERT INTO @table VALUES ('t')

DECLARE @minword char(5)
DECLARE @maxword char(5)

SET @minword='ai'
SET @maxword='t'
--SET @maxword='tb'--(remove the rem at the start of this line to unlock an extra test for comparison...)

--Query
SELECT word
FROM @table 
WHERE STUFF(word, LEN(word)+1, 5, 'aaaaa') BETWEEN STUFF(@minword, LEN(@minword)+1, 5, 'aaaaa') 
                                               AND STUFF(@maxword, LEN(@maxword)+1, 5, 'aaaaa')

Alternative solution based on your revised requirements:

DECLARE @testtable AS TABLE (word varchar(20))

INSERT INTO @testtable VALUES ('ly')
INSERT INTO @testtable VALUES ('Ly')
INSERT INTO @testtable VALUES ('Zoo')
INSERT INTO @testtable VALUES ('r')
INSERT INTO @testtable VALUES ('traci')

DECLARE @minword varchar(20)
DECLARE @maxword varchar(20)

SET @minword='ly'
SET @maxword='zol'

SELECT word, LEFT(word,LEN(@minword)), LEFT(word,LEN(@maxword)), @minword, @maxword
FROM @testtable 
WHERE LEFT(word,LEN(@minword))>=@minword
AND  LEFT(word,LEN(@maxword))<=@maxword

Другие советы

This should work:

SELECT * FROM table WHERE LEFT(word,3) BETWEEN 'a' AND 'ter'

There's no reason why BETWEEN shouldn't be able to compare your three-letter data string to the one-letter 'a'. Any 'axx' will be "greater than" just 'a' by itself, and so will be included.

If I understand you right you are trying to make this into a proc. If so, what you have will work in a proc with very little change. Something like the following (untested)...

CREATE PROC myProc(@low varchar(30), @high varchar(30)) AS 
SELECT * FROM table WHERE 
   (LEN(word) >= LEN(@low)
   AND 
   (LEN(word) >= LEN(@high)
   AND 
   (LEFT(word, LEN(@low)) >= @low) 
   AND 
   LEFT(word, LEN(@high)) <= @high

There are additional conditions to exclude records when 'word' is shorter than either of your parameters. Otherwise, you will get errors on the LEFT function. This may not be 100% but it should get you close.

I believe I've found a working solution. I'm not sure of the speed sacrifices here, but the DB will remain small, so it's a non-issue in my specific case.

I am using C# to build my SQL string with parameters. @lb is the lower bound word-part. @rb is the upper bound word-part. The where clause is inclusive, but could easily be change to exclusive as needed.

SELECT * FROM table
WHERE
CASE
    WHEN LEN(word) < LEN(@lb) THEN 0
    WHEN LEFT(word, LEN(@lb)) >= @lb THEN 1
    ELSE 0
END = 1
AND
CASE
    WHEN LEN(word) < LEN(@rb) THEN 1
    WHEN LEFT(word, LEN(@rb)) <= @rb THEN 1
    ELSE 0
END = 1
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top