Question

Lets say I have e Tables - Table1, Table2 and Table3. Table2 has foreign keys on Table1 and Table3. Table2 has 'Name' field. I want to select Table1.id and all matching names(concatenated) from Table3. Here is query for that

CREATE FUNCTION dbo.concat_string(@Input int)
   RETURNS VARCHAR(8000)
BEGIN
DECLARE @strList VARCHAR(8000)
SELECT @strList = COALESCE(@strList + ' ', '') + CAST(Table3.Name AS VARCHAR)
    FROM Table1
        JOIN Table2 ON Table2.Table1_ID = Table1.ID
        JOIN Table3 ON Table3.ID = Table2.Table3_ID
    WHERE
        Table1.ID = @Input
RETURN @strList;
END

SELECT Table1.ID, Table4.cc
FROM Table1 JOIN
(SELECT Table1.ID dbo.concat_string(Table1.ID) as "cc" FROM Table1) Table4 
ON Table1.ID = Table4.cID

It works the way I wanted to, but it is too slow. How can I optimize this? Any optimization will be helpfull.

Was it helpful?

Solution

This is because you are calling the function for each row of Table1, so that kills your performance.

The second point is that you are calling table1 many times with joins, that add some time.

I don't know if @strList = COALESCE(@strList + ' ', '') + CAST(Table3.Name AS VARCHAR) is less efficient than FOR XML PATH and STUFF to concatenate the multiple rows Name into a single row, but u can try it.

Also, I suggest you to do all your stuff in a unique query without using function (not the best for performance).

Try this :

SELECT
    t.ID
    ,STUFF((
        SELECT ' ,' + CAST(Table3.Name AS VARCHAR)
        FROM 
            Table1 as t1
            JOIN Table2 
                ON Table2.Table1_ID = Table1.ID
            JOIN Table3 
                ON Table3.ID = Table2.Table3_ID
        WHERE t1.ID = t.ID
        FOR XML PATH('')
    ), 1, 1, '') as "cc"
FROM 
    Table1 as t

Leave me a comment.

OTHER TIPS

You have hit a common problem. Storing data which is performant for inserting\updating\deleting or for selecting (OLTP v. OLAP).

If you constantly need to split your data and select from it, I would suggest you store the data already split BEFORE you need to select it. You can do the splitting using the above function into a separate table at the point your data is inserted or via an overnight process depending on how up-to-date you need it. You can then index the split data if additional performance needed.

So, in short:

  1. Create a new Table
  2. At the point of inserting your data, call your split function and insert your split data into your new table relating it to your main data
  3. Change any select statements to reference the new table

the down side to this approach is the added maintenance of a second table.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top