Ternary operator in SQL? “invalid length parameter

2019-07-22 03:59发布

问题:

Sorry for this misleading subject, i didn't know how to word better. Because i'm mainly a software-developer, the ternary operator comes to my mind with my following problem.

I need to find the most robust way to link two tables via nullable foreign-key(modModel and tabSparePart). The only similarity between both is the model's name and the sparepart's description(the tabSparePart is an external table from customer that is imported automatically, so it's not my responsibility and i cannot change the data).

Consider the following sparepart-names:

W200I_E/Swap
EXCHANGEUNIT P1i / SILVERBLACK/ CYRILLIC

The modelnames that i want to find are P1i and W200I_E. So there is only one strong rule that i can ensure in the where-clause:

  • there must be a separator / and the relevant part is the first one.

Here is the sample data:

Create table #temp(Partname varchar(100))
INSERT INTO #temp
    SELECT 'EXCHANGEUNIT P1i / SILVERBLACK/ CYRILLIC' UNION ALL SELECT 'W200I_E/Swap unit/Black' 

I would have been finished with following query:

SELECT RTRIM(LEFT(Partname, CHARINDEX('/', Partname) - 1)) AS UNIT
FROM #temp
WHERE CHARINDEX('/', Partname) > 0

... what returns:

EXCHANGEUNIT P1i
W200I_E

But i need P1i. So i need a way to handle also the case that the first part is separated by whitespaces. In that case i need to select the last word, but only if it is separated at all.

I'm getting a "invalid length parameter passed to the LEFT or SUBSTRING function"-error with following query:

SELECT REVERSE( LEFT( REVERSE(RTRIM(LEFT(Partname, CHARINDEX('/', Partname) - 1)))
    , CHARINDEX(' ', REVERSE(RTRIM(LEFT(Partname, CHARINDEX('/', Partname) - 1))))-1 ))
    AS Unit
FROM #temp
WHERE CHARINDEX('/', Partname) > 0

This would work without the second record that has no whitespace. If i would also ensure that the first part contains a whitespace, i would discard valid records.

To cut a long story short, I need to find a way to combine both ways according to the existence of separators.

PS: This has arisen from: Get the last word of a part of a varchar (LEFT/RIGHT)


If anybody is interested, this is the complete (working) stored-procedure. I'm sure i've never used such a strange JOIN:

CREATE PROC [dbo].[UpdateModelSparePart](@updateCount int output)
with execute as Owner
AS
    BEGIN
    BEGIN TRANSACTION

    UPDATE modModel SET fiSparePart=ModelPart.idSparePart
        FROM modModel INNER JOIN 
        (
          SELECT m.idModel
                ,m.ModelName
                ,sp.idSparePart
                ,sp.Price
                ,Row_Number()Over(Partition By idModel ORDER BY Price DESC)as ModelPrice
          FROM modModel AS m INNER JOIN tabSparePart AS sp 
            ON m.ModelName = CASE 
                WHEN CHARINDEX(' ', REVERSE(RTRIM(LEFT(sp.SparePartDescription, CHARINDEX('/', sp.SparePartDescription) - 1))))  > 0 THEN
                    REVERSE(  LEFT( REVERSE(RTRIM(LEFT(sp.SparePartDescription, CHARINDEX('/', sp.SparePartDescription) - 1)))
                    ,CHARINDEX(' ', REVERSE(RTRIM(LEFT(sp.SparePartDescription, CHARINDEX('/', sp.SparePartDescription) - 1))))-1 ))
                ELSE
                    RTRIM(LEFT(sp.SparePartDescription, CHARINDEX('/', sp.SparePartDescription) - 1))
                END
          WHERE (CHARINDEX('/', sp.SparePartDescription) > 0)
          GROUP BY idModel,ModelName,idSparePart,Price
        )As ModelPart
    ON ModelPart.idModel=modModel.idModel
    Where ModelPrice=1

    SET @updateCount = @@ROWCOUNT;

    COMMIT TRANSACTION

END

回答1:

A more concise version.

SELECT REVERSE(SUBSTRING(Rev, 0, CHARINDEX(' ', Rev))) AS Unit
FROM   #temp
       CROSS APPLY ( 
        SELECT REVERSE(RTRIM(LEFT(Partname, CHARINDEX('/', Partname) - 1))) + ' '
        ) T(Rev)
WHERE  CHARINDEX('/', Partname) > 0    


回答2:

I was able to solve the problem:

SELECT 'Unit' =
    CASE 
    WHEN CHARINDEX(' ', REVERSE(RTRIM(LEFT(Partname, CHARINDEX('/', Partname) - 1))))  > 0 THEN
         REVERSE( LEFT( REVERSE(RTRIM(LEFT(Partname, CHARINDEX('/', Partname) - 1)))
        ,CHARINDEX(' ', REVERSE(RTRIM(LEFT(Partname, CHARINDEX('/', Partname) - 1))))-1 ))
    ELSE 
        RTRIM(LEFT(Partname, CHARINDEX('/', Partname) - 1))
    END
FROM #temp
WHERE CHARINDEX('/', Partname) > 0

Ugly but working fine.