AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 295787
Accepted
Hannah Vernon
Hannah Vernon
Asked: 2021-07-17 12:38:50 +0800 CST2021-07-17 12:38:50 +0800 CST 2021-07-17 12:38:50 +0800 CST

Otimize a extração de dados json via OPENJSON

  • 772

Estou tentando otimizar a extração de valores obtidos de uma API REST que retorna valores json em uma matriz.

Aqui está um exemplo mínimo, completo e verificável que reflete exatamente o que estou fazendo.

USE tempdb;

DROP TABLE IF EXISTS dbo.json_test;

CREATE TABLE dbo.json_test
(
    json_test_id                int                 NOT NULL
        IDENTITY(1,1)
    , some_uniqueidentifier     uniqueidentifier    NULL
    , some_varchar              varchar(100)        NULL
    , the_json                  nvarchar(max)       NULL
);

INSERT INTO dbo.json_test (some_uniqueidentifier, some_varchar, the_json)
SELECT 
        some_uniqueidentifier       = NEWID()
      , some_varchar                = CONVERT(varchar(100), CRYPT_GEN_RANDOM(64), 1)
      , the_json = (
            SELECT st.* 
            FROM sys.tables st
                CROSS JOIN sys.tables st2
            WHERE st.object_id = t.object_id FOR JSON AUTO
            )
FROM sys.tables t;

;WITH src AS 
(
    SELECT jt.some_uniqueidentifier
        , jt.some_varchar
        , top_array.[key]
        , top_array.[value]
    FROM dbo.json_test jt
        CROSS APPLY OPENJSON(jt.the_json, N'lax $') top_array
),
src2 AS
(
    SELECT src.some_uniqueidentifier
        , src.some_varchar
        , src.[key]
        , src.[value]
        , inner_key = inner_array.[key]
        , inner_value = inner_array.[value]
    FROM src
        CROSS APPLY OPENJSON(src.[value], N'lax $') inner_array
)
SELECT src2.some_uniqueidentifier
    , src2.some_varchar
    , src2.[key]
    , [name]                                = MAX(CASE WHEN src2.[inner_key] = 'name'                               THEN src2.[inner_value] ELSE NULL END)
    , [object_id]                           = MAX(CASE WHEN src2.[inner_key] = 'object_id'                          THEN src2.[inner_value] ELSE NULL END)
    , [principal_id]                        = MAX(CASE WHEN src2.[inner_key] = 'principal_id'                       THEN src2.[inner_value] ELSE NULL END)
    , [schema_id]                           = MAX(CASE WHEN src2.[inner_key] = 'schema_id'                          THEN src2.[inner_value] ELSE NULL END)
    , [parent_object_id]                    = MAX(CASE WHEN src2.[inner_key] = 'parent_object_id'                   THEN src2.[inner_value] ELSE NULL END)
    , [type]                                = MAX(CASE WHEN src2.[inner_key] = 'type'                               THEN src2.[inner_value] ELSE NULL END)
    , [type_desc]                           = MAX(CASE WHEN src2.[inner_key] = 'type_desc'                          THEN src2.[inner_value] ELSE NULL END)
    , [create_date]                         = MAX(CASE WHEN src2.[inner_key] = 'create_date'                        THEN src2.[inner_value] ELSE NULL END)
    , [modify_date]                         = MAX(CASE WHEN src2.[inner_key] = 'modify_date'                        THEN src2.[inner_value] ELSE NULL END)
    , [is_ms_shipped]                       = MAX(CASE WHEN src2.[inner_key] = 'is_ms_shipped'                      THEN src2.[inner_value] ELSE NULL END)
    , [is_published]                        = MAX(CASE WHEN src2.[inner_key] = 'is_published'                       THEN src2.[inner_value] ELSE NULL END)
    , [is_schema_published]                 = MAX(CASE WHEN src2.[inner_key] = 'is_schema_published'                THEN src2.[inner_value] ELSE NULL END)
    , [lob_data_space_id]                   = MAX(CASE WHEN src2.[inner_key] = 'lob_data_space_id'                  THEN src2.[inner_value] ELSE NULL END)
    , [filestream_data_space_id]            = MAX(CASE WHEN src2.[inner_key] = 'filestream_data_space_id'           THEN src2.[inner_value] ELSE NULL END)
    , [max_column_id_used]                  = MAX(CASE WHEN src2.[inner_key] = 'max_column_id_used'                 THEN src2.[inner_value] ELSE NULL END)
    , [lock_on_bulk_load]                   = MAX(CASE WHEN src2.[inner_key] = 'lock_on_bulk_load'                  THEN src2.[inner_value] ELSE NULL END)
    , [uses_ansi_nulls]                     = MAX(CASE WHEN src2.[inner_key] = 'uses_ansi_nulls'                    THEN src2.[inner_value] ELSE NULL END)
    , [is_replicated]                       = MAX(CASE WHEN src2.[inner_key] = 'is_replicated'                      THEN src2.[inner_value] ELSE NULL END)
    , [has_replication_filter]              = MAX(CASE WHEN src2.[inner_key] = 'has_replication_filter'             THEN src2.[inner_value] ELSE NULL END)
    , [is_merge_published]                  = MAX(CASE WHEN src2.[inner_key] = 'is_merge_published'                 THEN src2.[inner_value] ELSE NULL END)
    , [is_sync_tran_subscribed]             = MAX(CASE WHEN src2.[inner_key] = 'is_sync_tran_subscribed'            THEN src2.[inner_value] ELSE NULL END)
    , [has_unchecked_assembly_data]         = MAX(CASE WHEN src2.[inner_key] = 'has_unchecked_assembly_data'        THEN src2.[inner_value] ELSE NULL END)
    , [text_in_row_limit]                   = MAX(CASE WHEN src2.[inner_key] = 'text_in_row_limit'                  THEN src2.[inner_value] ELSE NULL END)
    , [large_value_types_out_of_row]        = MAX(CASE WHEN src2.[inner_key] = 'large_value_types_out_of_row'       THEN src2.[inner_value] ELSE NULL END)
    , [is_tracked_by_cdc]                   = MAX(CASE WHEN src2.[inner_key] = 'is_tracked_by_cdc'                  THEN src2.[inner_value] ELSE NULL END)
    , [lock_escalation]                     = MAX(CASE WHEN src2.[inner_key] = 'lock_escalation'                    THEN src2.[inner_value] ELSE NULL END)
    , [lock_escalation_desc]                = MAX(CASE WHEN src2.[inner_key] = 'lock_escalation_desc'               THEN src2.[inner_value] ELSE NULL END)
    , [is_filetable]                        = MAX(CASE WHEN src2.[inner_key] = 'is_filetable'                       THEN src2.[inner_value] ELSE NULL END)
    , [is_memory_optimized]                 = MAX(CASE WHEN src2.[inner_key] = 'is_memory_optimized'                THEN src2.[inner_value] ELSE NULL END)
    , [durability]                          = MAX(CASE WHEN src2.[inner_key] = 'durability'                         THEN src2.[inner_value] ELSE NULL END)
    , [durability_desc]                     = MAX(CASE WHEN src2.[inner_key] = 'durability_desc'                    THEN src2.[inner_value] ELSE NULL END)
    , [temporal_type]                       = MAX(CASE WHEN src2.[inner_key] = 'temporal_type'                      THEN src2.[inner_value] ELSE NULL END)
    , [temporal_type_desc]                  = MAX(CASE WHEN src2.[inner_key] = 'temporal_type_desc'                 THEN src2.[inner_value] ELSE NULL END)
    , [history_table_id]                    = MAX(CASE WHEN src2.[inner_key] = 'history_table_id'                   THEN src2.[inner_value] ELSE NULL END)
    , [is_remote_data_archive_enabled]      = MAX(CASE WHEN src2.[inner_key] = 'is_remote_data_archive_enabled'     THEN src2.[inner_value] ELSE NULL END)
    , [is_external]                         = MAX(CASE WHEN src2.[inner_key] = 'is_external'                        THEN src2.[inner_value] ELSE NULL END)
    , [history_retention_period]            = MAX(CASE WHEN src2.[inner_key] = 'history_retention_period'           THEN src2.[inner_value] ELSE NULL END)
    , [history_retention_period_unit]       = MAX(CASE WHEN src2.[inner_key] = 'history_retention_period_unit'      THEN src2.[inner_value] ELSE NULL END)
    , [history_retention_period_unit_desc]  = MAX(CASE WHEN src2.[inner_key] = 'history_retention_period_unit_desc' THEN src2.[inner_value] ELSE NULL END)
    , [is_node]                             = MAX(CASE WHEN src2.[inner_key] = 'is_node'                            THEN src2.[inner_value] ELSE NULL END)
    , [is_edge]                             = MAX(CASE WHEN src2.[inner_key] = 'is_edge'                            THEN src2.[inner_value] ELSE NULL END)
FROM src2
GROUP BY src2.some_uniqueidentifier
    , src2.some_varchar
    , src2.[key]
ORDER BY src2.some_uniqueidentifier
    , src2.some_varchar
    , src2.[key];

O plano de consulta usa algumas junções de loops aninhados, independentemente de quantas linhas estão contidas na tabela de entrada. Presumivelmente, isso é um artefato de usar o CROSS APPLYoperador. Para sua diversão, já configurei um DB Fiddle .

Existe uma maneira mais eficiente de obter os dados do formato json em um conjunto "real" de colunas?

Criei o código acima usando minha instância local do SQL Server 2019, mas o destino será o Banco de Dados SQL do Azure, portanto, todas as opções mais recentes e melhores estão disponíveis.

sql-server azure-sql-database
  • 2 2 respostas
  • 991 Views

2 respostas

  • Voted
  1. Best Answer
    Biju jose
    2021-07-17T23:19:35+08:002021-07-17T23:19:35+08:00

    Você deve extrair diretamente os valores do array json usando a WITHcláusula, assim:

    SELECT src.*
        , tt.*
    FROM json_test src
    CROSS APPLY OPENJSON(src.the_json , 'lax $')
        WITH (
              [name]                               sysname       '$.name'
            , [object_id]                          int           '$.object_id'
            , [principal_id]                       int           '$.principal_id'
            , [schema_id]                          smallint      '$.schema_id'
            , [parent_object_id]                   int           '$.parent_object_id'
            , [type]                               char(2)       '$.type'
            , [type_desc]                          nvarchar(60)  '$.type_desc'
            , [create_date]                        datetime      '$.create_date'
            , [modify_date]                        datetime      '$.modify_date'
            , [is_ms_shipped]                      bit           '$.is_ms_shipped'
            , [is_published]                       bit           '$.is_published'
            , [is_schema_published]                bit           '$.is_schema_published'
            , [lob_data_space_id]                  int           '$.lob_data_space_id'
            , [filestream_data_space_id]           int           '$.filestream_data_space_id'
            , [max_column_id_used]                 int           '$.max_column_id_used'
            , [lock_on_bulk_load]                  bit           '$.lock_on_bulk_load'
            , [uses_ansi_nulls]                    bit           '$.uses_ansi_nulls'
            , [is_replicated]                      bit           '$.is_replicated'
            , [has_replication_filter]             bit           '$.has_replication_filter'
            , [is_merge_published]                 bit           '$.is_merge_published'
            , [is_sync_tran_subscribed]            bit           '$.is_sync_tran_subscribed'
            , [has_unchecked_assembly_data]        bit           '$.has_unchecked_assembly_data'
            , [text_in_row_limit]                  int           '$.text_in_row_limit'
            , [large_value_types_out_of_row]       bit           '$.large_value_types_out_of_row'
            , [is_tracked_by_cdc]                  bit           '$.is_tracked_by_cdc'
            , [lock_escalation]                    tinyint       '$.lock_escalation'
            , [lock_escalation_desc]               nvarchar(60)  '$.lock_escalation_desc'
            , [is_filetable]                       bit           '$.is_filetable'
            , [is_memory_optimized]                bit           '$.is_memory_optimized'
            , [durability]                         tinyint       '$.durability'
            , [durability_desc]                    nvarchar(60)  '$.durability_desc'
            , [temporal_type]                      tinyint       '$.temporal_type'
            , [temporal_type_desc]                 nvarchar(60)  '$.temporal_type_desc'
            , [history_table_id]                   int           '$.history_table_id'
            , [is_remote_data_archive_enabled]     bit           '$.is_remote_data_archive_enabled'
            , [is_external]                        bit           '$.is_external'
            , [history_retention_period]           int           '$.history_retention_period'
            , [history_retention_period_unit]      int           '$.history_retention_period_unit'
            , [history_retention_period_unit_desc] nvarchar(10)  '$.history_retention_period_unit_desc'
            , [is_node]                            bit           '$.is_node'
            , [is_edge]                            bit           '$.is_edge'
        ) AS tt
    

    Este Fiddle compara a saída de ambos os métodos provando que os resultados são os mesmos.

    A única coluna que falta na saída é o keyvalor gerado pela OPENJSONfunção com valor de tabela que só é retornada quando não há WITHcláusula especificada. O plano para minha variante tem apenas um único loop aninhado e parece ser muito mais eficiente.

    insira a descrição da imagem aqui

    Exemplo do Microsoft Docs para OPENJSON CROSS APPLY

    • 7
  2. D-K
    2021-09-25T08:33:23+08:002021-09-25T08:33:23+08:00

    Eu encontrei isso recentemente e é uma maneira de acelerar o acesso ao json com índices semelhantes a velocidades:

    https://bertwagner.com/posts/one-sql-cheat-code-for-amazingly-fast-json-queries/

    Não tenho certeza se isso é permitido, mas vou copiar e colar parte do blog aqui:

    Recentemente tenho trabalhado muito com JSON no SQL Server 2016 .

    Uma das hesitações que muitas pessoas têm ao usar JSON no SQL Server é que elas acham que a consulta deve ser muito lenta – o SQL deve se destacar em dados relacionais, não em análise de strings, certo?

    Acontece que o desempenho é muito bom com as funções autônomas do SQL Server JSON. Ainda melhor é que é possível fazer consultas em dados JSON executados em velocidades absurdas usando índices em colunas computadas analisadas JSON. Neste post, quero dar uma olhada em como o SQL é capaz de analisar* com um desempenho tão bom.

    *"Parse" aqui é na verdade uma mentira - está fazendo outra coisa nos bastidores. Você verá o que quero dizer, continue lendo!

    • 1

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

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

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve