AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 179482
Accepted
Jon49
Jon49
Asked: 2017-07-11 07:51:53 +0800 CST2017-07-11 07:51:53 +0800 CST 2017-07-11 07:51:53 +0800 CST

多语言数据库的排序代码重用

  • 772

我正在尝试从多语言表中对结果进行排序。我想让排序算法来自一个函数,但它至少会增加 8% 的性能。所以,我不太确定该怎么做。因此,对于排序,我使用了一篇关于如何对多语言表格进行排序的文章中描述的方法,如下所示:

select UnicodeData,Collation
from (
    select
        ML.UnicodeData,
        ML.Collation,
        RN =
            CASE
                when Collation = 'he-IL' then ROW_NUMBER() OVER (order by unicodedata Collate Hebrew_CI_AS                  )
                when Collation = 'en-US' then ROW_NUMBER() OVER (order by unicodedata Collate SQL_Latin1_General_CP1_CI_AS  )
                when Collation = 'kn-IN' then ROW_NUMBER() OVER (order by unicodedata Collate Indic_General_100_CI_AS       )
                when Collation = 'hi-IN' then ROW_NUMBER() OVER (order by unicodedata Collate Indic_General_100_CI_AS       )
                when Collation = 'ar-EG' then ROW_NUMBER() OVER (order by unicodedata Collate Arabic_CI_AS                  )
                when Collation = 'cs'    then ROW_NUMBER() OVER (order by unicodedata Collate Czech_CI_AS                   )
            END
    from MultipleLanguages ML
) T
order by RN

除了我将collation代码抽象到它自己的函数中,就像这样:

CREATE FUNCTION [utils].[OrderByLanguage]
( @LanguageID tinyint
, @IDName utils.ID_Name READONLY
) RETURNS TABLE AS RETURN
SELECT
      t.ID
    , CASE @LanguageID
        WHEN 1 THEN ROW_NUMBER() OVER (ORDER BY t.[Name]) -- en
        WHEN 3 THEN ROW_NUMBER() OVER (ORDER BY t.[Name]) -- en-ca
        WHEN 6 THEN ROW_NUMBER() OVER (ORDER BY t.[Name]) -- 'en-nz'
        WHEN 5 THEN ROW_NUMBER() OVER (ORDER BY t.[Name]) -- 'en-za'
        WHEN 2 THEN ROW_NUMBER() OVER (ORDER BY t.[Name] COLLATE Modern_Spanish_CI_AI) -- es
        WHEN 4 THEN ROW_NUMBER() OVER (ORDER BY t.[Name] COLLATE French_CI_AI) -- 'fr-ca'
    END RowNumber
FROM @IDName t

但是当我调用这个函数时,我必须对表值函数进行这种笨拙的双重调用。

CREATE FUNCTION api.GetTable
( @LanguageCode VARCHAR(10)
) RETURNS NVARCHAR(MAX)
AS BEGIN

    DECLARE
          @Result NVARCHAR(MAX)
        , @LangID tinyint
    DECLARE @Sort utils.ID_Name

    SET @LangID = api_utils.GetLanguageID(@LanguageCode)

    INSERT INTO @Sort (ID, [Name])
    SELECT
          t.ID
        , t.title
    FROM api_utils.GetTable(@LangID) t

    SET @Result = (
        SELECT
            CONVERT(VARCHAR(10), t.ID) id,
            t.category,
            t.[system],
            t.title,
            JSON_QUERY(utils.ToRawJsonArray((
                SELECT x.[Description]
                FROM api_utils.GetKeywords(t.ID, @LangID) x
                ORDER BY x.[Description]
                FOR JSON AUTO), 'Description')
            ) keywords
        FROM api_utils.GetTable(@LangID) t
        ORDER BY (SELECT s.RowNumber
                  FROM utils.OrderByLanguage(@LangID, @Sort) s
                  WHERE s.ID = t.ID)
        FOR JSON AUTO, ROOT('titles')
        )

    RETURN @Result

END

所以,你可以看到我必须调用函数api_utils.GetTable两次。据我所知,提取归类排序的唯一其他方法是放入实际的排序算法,然后有一个脚本来搜索所有代码库,并在我需要添加另一种语言时添加另一种归类语言。还有其他方法吗?其他人做了什么?最佳做法是什么?这方面的性能并不是绝对关键,但保持精简是很好的,所以不会花太长时间,因为它已经是一个密集的呼叫。

提前致谢!

更新

在评论中回答@srutzky 的问题:

1) api_utils.GetTable返回了多少数据?

表中返回了大约 150 条记录。

2) 为什么第一次将结果转储到@Sort 时调用api_utils.GetTable 两次?

该@Sort表是内存优化的用户定义表 ( UDT)。因为我将一个表传递给utils.OrderByLanguage函数,所以它需要是一个UDT. 这意味着我需要api_utils.GetTable两次从内联函数中获取数据。我不确定它是否导致性能问题调用api_utils.GetTable两次。也许SQL Server足够聪明来缓存结果?再次测试INSERT查询成本为 38%。因此,查询成本的很大一部分。

将类别和系统列添加到 @Sort 并在第一次调用中将它们拉回,然后在 FROM 子句中使用 @Sort 不是更快吗?

由于UDT是通用的,可用于调用该函数的所有不同过程,utils.OrderByLanguage因此很难对不同过程将使用的未知数量的列进行概括。

3)这必须是一个函数还是可以是一个存储过程?

你在说什么api_utils.GetTable?我宁愿api_utils.GetTable保留一个功能,因为它更容易使用和测试。我称之为api_utils.GetTable.Stored Procedure

如果你在谈论utils.OrderByLanguage我不介意它是否是一个stored procedure. 我不确定这会有什么帮助。所以,如果可以,请告诉我!

更新为已接受的答案

我发现添加一个索引并没有在性能方面产生影响。我还认为我不妨将该sort列放在原始#sort表中,因为无论如何它都必须相同。这减少了我的 SSDT 项目中的警告数量。然后我就像这样alter在专栏上做一个:

ALTER TABLE #AlterSort ALTER COLUMN [sort] nvarchar(max) COLLATE SQL_Latin1_General_CP1_CI_AS
sql-server t-sql
  • 2 2 个回答
  • 297 Views

2 个回答

  • Voted
  1. Best Answer
    Solomon Rutzky
    2017-07-14T22:24:15+08:002017-07-14T22:24:15+08:00

    看看你到目前为止所拥有的,[utils].[OrderByLanguage]内联表值函数 (ITVF) 很好,但它仍然看起来是一个相关的子查询,其中对于其中的每一行,api_utils.GetTable(@LangID)它都会传入所有行以api_utils.GetTable(@LangID)对其进行排序(即ORDER BY子句。

    在运行时应用排序规则可能会非常昂贵,因为它必须在那一刻为这些值生成排序键。为了获得最佳性能,创建索引将提前生成排序键,甚至将它们按正确的顺序排列。但是处理多个语言环境确实很棘手。根据字符串的大小、需要多少个区域设置/排序规则以及将超过多少行,为每个区域设置创建源列的副本可能需要大量额外的磁盘空间(和 I/O)未来 3 - 5 年。幸运的是,您可以创建非持久计算列作为那些副本(不占用任何空间)并索引那些(确实占用空间)。虽然如果有 10 个语言环境,这可能不可行,但至少有一个基本列NVARCHAR(200)和 100 万(或更多)行,对于您的情况,它应该可以正常工作。使用这种方法,动态部分将是选择要从中选择的列(可通过动态 SQL 或IF语句实现,具体取决于情况)。但正如您在以下示例中看到的(打开“包括实际执行计划”),两个过滤查询(最后 2 个查询)都在预期索引上获取索引搜索并返回预期结果:

    您可以在 dbfiddle.uk 上看到现场演示。

    SET NOCOUNT ON;
    -- DROP TABLE #T;
    CREATE TABLE #T
    (
      [ID] INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
      [Col1] VARCHAR(10) COLLATE Latin1_General_100_CI_AS,
      [Col2] AS ([Col1] COLLATE Latin1_General_100_CS_AS)
    );
    
    CREATE INDEX [IX_#T_Col1] ON [#T] ([Col1]);
    CREATE INDEX [IX_#T_Col2] ON [#T] ([Col2]);
    
    INSERT INTO #T ([Col1]) VALUES ('a'), ('A');
    
    SELECT * FROM #T; -- 2 rows
    
    SELECT [ID] FROM #T WHERE [Col1] = 'A'; -- 2 rows
    
    SELECT [ID] FROM #T WHERE [Col2] = 'A'; -- 1 row
    

    但是,鉴于此代码api.GetTable可能不是最佳方法。如果您想保留当前结构(尽可能多),那么您可以执行以下操作:

    1. 转换api.GetTable为存储过程
    2. 使用OUTPUT参数,这样你就不需要处理结果集
    3. @Sort应该是临时表,不是表变量
    4. 使用除/之外所需#Sort的所有列创建临时表:、、和。[Name]titleIDcategory[system]
    5. 在一系列IF语句中,添加[title]列,但使用正确的排序规则:

      IF (@LanguageID = 2)
      BEGIN
        ALTER TABLE #Sort ADD [title] NVARCHAR(2000) COLLATE Modern_Spanish_CI_AI
      END;
      
      IF (@LanguageID = 4)
      BEGIN
        ALTER TABLE #Sort ADD [title] NVARCHAR(2000) COLLATE French_CI_AI
      END;
      
      ...
      
    6. FROM api_utils.GetTable(@LangID) t主@Result=查询更改为:FROM #Sort t
    7. ORDER BY (SELECT s.RowNumber...主@Result=查询更改为:ORDER BY t.[title]

    这将需要在每次执行时重新应用排序规则,但是:

    1. 它不需要对基表进行任何更改
    2. 它将重新使用返回的数据api.GetTable(没有双重调用)
    3. 它将使用[Name]列上的统计信息
    4. 它不会是一个相关的子查询(呜呼!)
    5. [Name]它为您提供了在列被填充后创建索引的选项。

    代码重用

    通过切换到存储过程和临时表,我们实际上可以实现代码重用的目标。将排序列添加到本地临时表的代码可以抽象为另一个存储过程。虽然在子过程调用中创建临时表没有帮助,因为一旦该子过程调用结束该临时表将消失,对子过程调用之前存在的临时表所做的更改将在该调用完成后继续存在. 例如:

    设置

    CREATE PROCEDURE #AddSortColumn
    (
      @LanguageID TINYINT
    )
    AS
    SET NOCOUNT ON;
    
    DECLARE @IsColumnAdded BIT = 0;
    
    IF (@LanguageID = 2)
    BEGIN
      ALTER TABLE #Sort ADD [title] NVARCHAR(2000) COLLATE Modern_Spanish_CI_AI;
      SET @IsColumnAdded = 1;
    END;
    
    IF (@LanguageID = 4)
    BEGIN
      ALTER TABLE #Sort ADD [title] NVARCHAR(2000) COLLATE French_CI_AI;
      SET @IsColumnAdded = 1;
    END;
    
    IF (@IsColumnAdded = 0)
    BEGIN
      RAISERROR(N'Invalid @LanguageID: %d', 16, 1, @LanguageID);
      RETURN;
    END;
    GO
    

    测试

    CREATE TABLE #Sort (ID INT);
    
    EXEC tempdb.dbo.sp_help '#Sort'; -- only 1 column
    
    EXEC #AddSortColumn @LanguageID = 4;
    
    EXEC tempdb.dbo.sp_help '#Sort'; -- now there are 2 columns
    

    通过仅将特定排序规则放在这个存储过程中,在添加要支持的新语言时,您应该只有这个地方可以更新。

    把它们放在一起

    考虑到以上所有内容,我们最终得到以下存储过程,其中包含对 的单个调用api_utils.GetTable(),没有相关的子查询,并且 api_utils.AddSortColumn(您需要创建)可以在其他存储过程中使用并针对其他基表:

    CREATE PROCEDURE api.GetTableAsJSON
    (
      @LanguageCode VARCHAR(10),
      @JSONifiedTable NVARCHAR(MAX) OUTPUT
    )
    AS
    SET NOCOUNT ON;
    
        DECLARE @LangID TINYINT;
    
        CREATE TABLE #Sort
        (
          [ID] INT NOT NULL PRIMARY KEY,
          [category] ...,
          [system] ...
        );
    
        SET @LangID = api_utils.GetLanguageID(@LanguageCode)
    
        EXEC api_utils.AddSortColumn @LanguageID = @LangID; -- add [Title] to #Sort
    
        INSERT INTO #Sort ([ID], [category], [system], [Title])
          SELECT t.[ID], t.[category], t.[system], t.[title]
          FROM   api_utils.GetTable(@LangID) t
    
        -- Optional index; try with and without to see which is better.
        --CREATE INDEX [IX_#Sort_Title] ON #Sort ([Title]);
    
        SET @JSONifiedTable = (
            SELECT
                CONVERT(VARCHAR(10), t.ID) AS [id],
                t.[category],
                t.[system],
                t.title,
                JSON_QUERY(utils.ToRawJsonArray((
                    SELECT x.[Description]
                    FROM api_utils.GetKeywords(t.[ID], @LangID) x
                    ORDER BY x.[Description]
                    FOR JSON AUTO), 'Description')
                ) AS [keywords]
            FROM #Sort t
            ORDER BY t.[Title]
            FOR JSON AUTO, ROOT('titles')
            );
    
        RETURN;
    END;
    
    • 2
  2. Jon49
    2017-07-15T15:41:08+08:002017-07-15T15:41:08+08:00

    另一种方法是只拥有一个代码生成器。这是如果你需要最大性能,因为它只比标记为我给定参数的答案的解决方案快 14%(当你在多个地方和同一代码的多个部分使用它时,给定的 14% 加起来相当快) .

    # Place the following in your code then run this script and it will update for you
    # without the `#` comment symbols. Replace `title` with whatever it field it should
    # be sorting by.
    
    #-- Start-Collation-Sort {sort-by:title}
    #
    #-- End-Collation-Sort
    
    $text = @'
    -- Start-Collation-Sort {sort-by:$1,table:$2}
    -- This is generated code. Use ./CollationSortGenerator.ps1 to change
    -- this code and all the other files which contain this code.
      CASE @LangID
        WHEN 1 THEN ROW_NUMBER() OVER (ORDER BY $2.$1) -- en
        WHEN 3 THEN ROW_NUMBER() OVER (ORDER BY $2.$1) -- en-ca
        WHEN 6 THEN ROW_NUMBER() OVER (ORDER BY $2.$1) -- 'en-nz'
        WHEN 5 THEN ROW_NUMBER() OVER (ORDER BY $2.$1) -- 'en-za'
        WHEN 2 THEN ROW_NUMBER() OVER (ORDER BY $2.$1 COLLATE Modern_Spanish_CI_AI) -- es
        WHEN 4 THEN ROW_NUMBER() OVER (ORDER BY $2.$1 COLLATE French_CI_AI) -- 'fr-ca'
      END
    -- `End-Collation-Sort
    '@
    
    $regex =
    @'
    (?m)^\s*-- Start-Collation-Sort {sort-by:(.+),table:(.+)}
    [^`]*
    -- `End-Collation-Sort
    '@
    
    Get-ChildItem -Path *.sql -recurse |
        Where-Object { (Select-String "-- Start-Collation-Sort"  $_ -Quiet -CaseSensitive -SimpleMatch) } |
        ForEach-Object {
            Write-Output ("Updating file ... " + ($_ | Resolve-Path -Relative ))
    
            (Get-Content $_ -Raw) -replace $regex, $text |
            Set-Content $_
        }
    
    • 1

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve