假设 STRING_SPIT SQL 操作中的行顺序

Assuming Row order in a STRING_SPIT SQL Operation

我有一个存储过程需要假设行顺序始终相同,以便正确 运行 否则我会得到不正确的数据。用户提供需要查询的 ID 字符串,存储过程使用 STRING_SPLIT(@Parameter, ',') 操作将此字符串放入 table 中。

可以安全地假设字符串将按照它们被赋予 table 的顺序放入 table 中吗?我创建了一个单元测试 运行 10000 次,每次检查 table 的顺序并通过。因此,我猜测做出这个假设是安全的。

SQL:

CREATE PROCEDURE [dbo].[sp_TestOrder] @TestIDs varchar(5000)
AS

IF(OBJECT_ID('tempdb..#TestNames') IS NOT NULL) BEGIN DROP TABLE #TestNames END

-- INTO #FundNames
SELECT RTRIM(LTRIM(value)) AS TestIDs
    INTO #TestNames
    FROM string_split(@TestIDs, ',');

ALTER TABLE #TestNames ADD RowNumber INT IDENTITY(1,1)

SELECT * FROM #TestNames

IF(OBJECT_ID('tempdb..#TestNames') IS NOT NULL) BEGIN DROP TABLE #TestNames END

GO

C# 中的单元测试

[TestMethod]
public void SQLOrderTest()
{
    // Arrange
    string testIds = "3, 5, 1, 10";
    string spName = "[dbo].[sp_TestOrder]";
    string ServerName = PARR_DBDataObject.ServerName;
    string DBName = PARR_DBDataObject.DBName;

    // Act 
    bool results = true;

    for(int i = 0; i < 10000; i++)
    {
        DataTable dt = SQL.QueryStoredProcedure(spName, DBName, ServerName, false, 5000,
                                             new Tuple<string, object>("@TestIDs", testIds));
        // First Row            
        if (GetRowNumber(dt, "3") != 1) results = false;

        // Second Row            
        if (GetRowNumber(dt, "5") != 2) results = false;

        // Third Row
        if (GetRowNumber(dt, "1") != 3) results = false;

        // Forth Row 
        if (GetRowNumber(dt, "10") != 4) results = false;
    }

    Assert.AreEqual(results, true);
}

private int GetRowNumber(DataTable dt, string TestIDValue)
{
    var row = from r in dt.AsEnumerable()
              where r.Field<string>("TestIDs") == TestIDValue
              select new
              {
                  TestID = r.Field<string>("TestIDs"),
                  rowNum = r.Field<Int32>("RowNumber")

              };
    return row.First().rowNum;
}

SQL 表表示 无序 集合。除非您使用 ORDER BY,否则假设数据按特定顺序排列永远不是一个好主意。通常,单个页面上的小型数据集甚至会按照您的预期一致地排序。但是使用被记录为不正确的功能仍然是危险的。

如果你想保证排序,那么你可以插入带有order by的记录和一个排序值。这是使用递归 CTE 的方法:

with cte as (
      select convert(varchar(max), left(@testids, charindex(',', @testids + ',') - 1)) as id,
             convert(varchar(max), stuff(@testids, 1, charindex(',', @testids + ','), '')) as rest,
             1 as lev
      union all
      select convert(varchar(max), left(rest, charindex(',', rest + ',') - 1)) as id,
             convert(varchar(max), stuff(rest, 1, charindex(',', rest + ','), '')),
             lev + 1 as lev
      from cte
      where rest <> ''
     )
select trim(id) as id, lev
into #testnames
from cte
where trim(id) <> ''
order by lev;  -- not really needed but handy if you have an identity column

Here 是一个 db<>fiddle.

您可以使用以下方式检索它们:

select tn.*
from #testnames tn
order by lev;