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 / 问题

All perguntas(dba)

Martin Hope
Paul White
Asked: 2016-05-09 06:07:58 +0800 CST

Leituras lógicas diferentes ao acessar os mesmos dados LOB

  • 26

Aqui estão três testes simples que leem os mesmos dados, mas relatam leituras lógicas muito diferentes:

Configurar

O script a seguir cria uma tabela de teste com 100 linhas idênticas, cada uma contendo uma coluna xml com dados suficientes para garantir que ela seja armazenada fora da linha. No meu banco de dados de teste, o comprimento do xml gerado é de 20.204 bytes para cada linha.

-- Conditional drop
IF OBJECT_ID(N'dbo.XMLTest', N'U') IS NOT NULL
    DROP TABLE dbo.XMLTest;
GO
-- Create test table
CREATE TABLE dbo.XMLTest
(
    ID integer IDENTITY PRIMARY KEY,
    X xml NULL
);
GO
-- Add 100 wide xml rows
DECLARE @X xml;

SET @X =
(
    SELECT TOP (100) *
    FROM  sys.columns AS C
    FOR XML 
        PATH ('row'),
        ROOT ('root'),
        TYPE
);

INSERT dbo.XMLTest
    (X)
SELECT TOP (100)
    @X
FROM  sys.columns AS C;

-- Flush dirty buffers
CHECKPOINT;

testes

Os três testes a seguir leem a coluna xml com:

  1. SELECTUma declaração simples
  2. Atribuindo o xml a uma variável
  3. Usando SELECT INTOpara criar uma tabela temporária
-- No row count messages or graphical plan
-- Show I/O statistics
SET NOCOUNT ON;
SET STATISTICS XML OFF;
SET STATISTICS IO ON;
GO
PRINT CHAR(10) + '=== Plain SELECT ===='

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

SELECT XT.X 
FROM dbo.XMLTest AS XT;
GO
PRINT CHAR(10) + '=== Assign to a variable ===='

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

DECLARE @X xml;

SELECT
    @X = XT.X
FROM dbo.XMLTest AS XT;
GO
PRINT CHAR(10) + '=== SELECT INTO ===='

IF OBJECT_ID(N'tempdb..#T', N'U') IS NOT NULL
    DROP TABLE #T;

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

SELECT 
    XT.X
INTO #T
FROM dbo.XMLTest AS XT
GO
SET STATISTICS IO OFF;

Resultados

A saída é:

=== SELEÇÃO Simples ====
Tabela 'XMLTest'. Contagem de varredura 1, leituras lógicas 3, leituras físicas 1, leituras antecipadas 0,
    lob lógico lê 795, lob físico lê 37, lob read-ahead lê 796.

=== Atribuir a uma variável ====
Tabela 'XMLTest'. Contagem de varredura 1, leituras lógicas 3, leituras físicas 1, leituras antecipadas 0,
    lob leituras lógicas 0, lob leituras físicas 0, leitura antecipada de lob 0.

=== SELECIONE EM ====
Tabela 'XMLTest'. Contagem de varredura 1, leituras lógicas 3, leituras físicas 1, leituras antecipadas 0,
    lob lógico lê 300, lob físico lê 37, lob read-ahead lê 400.

Perguntas

  • Por que as leituras de LOB são tão diferentes?
  • Certamente os mesmos dados exatos foram lidos em cada teste?
sql-server performance
  • 1 respostas
  • 1367 Views
Martin Hope
Greg Burghardt
Asked: 2016-03-29 07:13:56 +0800 CST

Que problemas são resolvidos dividindo endereços de rua em colunas individuais?

  • 26

Temos uma equipe que projeta as tabelas e relações para desenvolvedores de software. Em nossa organização, eles são bastante rigorosos quanto à aplicação da normalização 3NF - o que, para ser honesto, concordo, dado o tamanho de nossa organização e como as necessidades de nossos clientes mudam com o tempo. Há apenas uma área que não tenho certeza sobre as razões por trás de sua decisão de design: endereços.

Embora isso se concentre principalmente em endereços nos Estados Unidos, acho que isso pode se aplicar a qualquer país que faça isso. Cada parte de um endereço obtém sua própria coluna na tabela de endereços. Por exemplo, pegue este endereço retorcido nos EUA:

Attn: Jane Doe
485 1/2 N Smith St SW, APT 300B
Chicago, IL 11111-2222

Ele seria dividido no banco de dados assim:

  • Número da rua: 485
  • Fracção de rua: 1/2
  • Rua pré-direcional: N (Norte)
  • Nome da rua: Smith
  • Tipo de rua: ST (Rua)
  • Rua pós-direcional: SW (Sudoeste)
  • Cidade: Chicago
  • Estado: IL (Illinois)
  • CEP: 11111
  • CEP4: 2222
  • País (presume-se que seja EUA)
  • Atenção: Jane Doe
  • Caixa Postal: NULO
  • Tipo de habitação: APT (Apartamento)
  • Número da residência: 300B

E haveria algumas outras colunas relacionadas a rotas rurais e rotas contratadas. Além disso, nosso aplicativo específico provavelmente terá alguns endereços internacionais. Os modeladores de dados disseram que adicionariam colunas específicas para endereços internacionais, que seriam os campos normais da linha 1, linha 2.

No começo eu pensei que isso era MUITO exagerado. A pesquisa on-line refere-se repetidamente ao uso da linha de endereço 1, 2, 3 e possivelmente 4 e, em seguida, dividindo cidade, região e código postal. Temos um caso de uso para nosso novo aplicativo em que essa granularidade é benéfica. Temos que validar se o usuário não está criando um negócio duplicado, e verificar o endereço é uma das validações. Podemos fazê - lo funcionar com as linhas de endereço 1 e 2, mas seria mais difícil.

Quanto ao nosso aplicativo específico, precisamos armazenar vários tipos de endereços para empresas e pessoas (físico, postal, envio etc.). Podemos precisar gerar cartas de formulário imprimíveis, mas esse requisito não foi discutido até agora .

Algumas outras coisas que os aplicativos em nossa organização precisam suportar:

  • Auditoria (com tabelas de histórico completas)
  • Imprimindo etiquetas de endereçamento
  • Gerando formulários impressos
  • Relatórios (para governos nacionais e regionais)

Embora nosso aplicativo possa não estar fazendo tudo o que todos os outros aplicativos estão fazendo, dividir endereços em vários componentes é um padrão corporativo onde trabalho. Independentemente de nosso aplicativo se beneficiar disso, somos forçados a fazer isso.

Pergunta semi-relacionada do StackOverflow: onde está um bom analisador de endereços que foi fechado, mas ilustra como os endereços de análise podem ser difíceis.

Para que eu entenda melhor a decisão de design deles e venda a ideia ao nosso cliente...

Que problemas são resolvidos dividindo o endereço em colunas individuais?

Pontos de bônus para quem implementou um sistema como esse, porque teve problemas.

database-design normalization
  • 7 respostas
  • 6307 Views
Martin Hope
Erik Darling
Asked: 2016-03-17 12:16:45 +0800 CST

Cláusula SARGable WHERE para duas colunas de data

  • 26

Eu tenho o que é, para mim, uma pergunta interessante sobre SARGability. Neste caso, trata-se de usar um predicado na diferença entre duas colunas de data. Aqui está a configuração:

USE [tempdb]
SET NOCOUNT ON  

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

SELECT TOP 1000
IDENTITY (BIGINT, 1,1) AS ID,
CAST(DATEADD(DAY, [m].[severity] * -1, GETDATE()) AS DATE) AS [DateCol1],
CAST(DATEADD(DAY, [m].[severity], GETDATE()) AS DATE) AS [DateCol2]
INTO #sargme
FROM sys.[messages] AS [m]

ALTER TABLE [#sargme] ADD CONSTRAINT [pk_whatever] PRIMARY KEY CLUSTERED ([ID])
CREATE NONCLUSTERED INDEX [ix_dates] ON [#sargme] ([DateCol1], [DateCol2])

O que vejo com bastante frequência é algo assim:

/*definitely not sargable*/
SELECT
    * ,
    DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2])
FROM
    [#sargme] AS [s]
WHERE
    DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2]) >= 48;

...que definitivamente não é SARGable. Isso resulta em uma varredura de índice, lê todas as 1000 linhas, não é bom. Linhas estimadas fedem. Você nunca colocaria isso em produção.

Não senhor, não gostei.

Seria bom se pudéssemos materializar CTEs, porque isso nos ajudaria a tornar isso, bem, mais SARGable-er, tecnicamente falando. Mas não, temos o mesmo plano de execução do topo.

/*would be nice if it were sargable*/
WITH    [x] AS ( SELECT
                * ,
                DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2]) AS [ddif]
               FROM
                [#sargme] AS [s])
     SELECT
        *
     FROM
        [x]
     WHERE
        [x].[ddif] >= 48;

E, claro, como não estamos usando constantes, esse código não muda nada e nem é meio SARGable. Sem graça. Mesmo plano de execução.

/*not even half sargable*/
SELECT
    * ,
    DATEDIFF(DAY, [s].[DateCol1], [s].[DateCol2])
FROM
    [#sargme] AS [s]
WHERE
    [s].[DateCol2] >= DATEADD(DAY, 48, [s].[DateCol1])

Se você estiver com sorte e estiver obedecendo a todas as opções ANSI SET em suas strings de conexão, poderá adicionar uma coluna computada e pesquisá-la ...

ALTER TABLE [#sargme] ADD [ddiff] AS 
DATEDIFF(DAY, DateCol1, DateCol2) PERSISTED

CREATE NONCLUSTERED INDEX [ix_dates2] ON [#sargme] ([ddiff], [DateCol1], [DateCol2])

SELECT [s].[ID] ,
       [s].[DateCol1] ,
       [s].[DateCol2]
FROM [#sargme] AS [s]
WHERE [ddiff] >= 48

Isso resultará em uma busca de índice com três consultas. O estranho é onde adicionamos 48 dias a DateCol1. A consulta DATEDIFFna WHEREcláusula, o CTE, e a consulta final com um predicado na coluna computada fornecem um plano muito melhor com estimativas muito melhores e tudo mais.

Eu poderia viver com isso.

O que me leva à pergunta: em uma única consulta, existe uma maneira SARGable de realizar essa pesquisa?

Sem tabelas temporárias, sem variáveis ​​de tabela, sem alterar a estrutura da tabela e sem exibições.

Estou bem com junções automáticas, CTEs, subconsultas ou várias passagens pelos dados. Pode funcionar com qualquer versão do SQL Server.

Evitar a coluna computada é uma limitação artificial porque estou mais interessado em uma solução de consulta do que em qualquer outra coisa.

sql-server index
  • 5 respostas
  • 3533 Views
Martin Hope
Oliver Salzburg
Asked: 2016-02-17 07:59:22 +0800 CST

Como obter o ID da linha conflitante no upsert?

  • 26

Tenho uma tabela tagcom 2 colunas: id(uuid) e name(texto). Agora quero inserir uma nova tag na tabela, mas se a tag já existir, quero simplesmente obter o idregistro existente.

Presumi que poderia usar apenas ON CONFLICT DO NOTHINGem combinação com RETURNING "id":

INSERT INTO
    "tag" ("name")
VALUES( 'foo' )
ON CONFLICT DO NOTHING
RETURNING "id";

Mas isso retorna um conjunto de resultados vazio, se a tag com o nome "foo" já existir.

Em seguida, alterei a consulta para usar uma DO UPDATEcláusula noop:

INSERT INTO
    "tag" ("name")
VALUES( 'foo' )
ON CONFLICT ("name") DO UPDATE SET "name" = 'foo'
RETURNING "id";

Isso funciona como pretendido, mas é um pouco confuso, porque estou apenas definindo o nome para o valor já existente.

Esta é a maneira de resolver esse problema ou há uma abordagem mais simples que estou perdendo?

postgresql upsert
  • 2 respostas
  • 17976 Views
Martin Hope
boot4life
Asked: 2016-01-07 12:14:54 +0800 CST

O que a posição da cláusula ON realmente significa?

  • 26

A sintaxe normal JOIN ... ON ...é bem conhecida. Mas também é possível posicionar a ONcláusula separada daquela a JOINque corresponde. Isso é algo que raramente é visto na prática, não é encontrado em tutoriais e não encontrei nenhum recurso da web que sequer mencione que isso é possível.

Aqui está um script para brincar:

SELECT *
INTO #widgets1
FROM (VALUES (1), (2), (3)) x(WidgetID)


SELECT *
INTO #widgets2
FROM (VALUES (1, 'SomeValue1'), (2, 'SomeValue2'), (3, 'SomeValue3')) x(WidgetID, SomeValue)

SELECT *
INTO #widgetProperties
FROM (VALUES
    (1, 'a'), (1, 'b'),
    (2, 'a'), (2, 'b'))
x(WidgetID, PropertyName)


--q1
SELECT w1.WidgetID, w2.SomeValue, wp.PropertyName
FROM #widgets1 w1
LEFT JOIN #widgets2 w2 ON w2.WidgetID = w1.WidgetID
LEFT JOIN #widgetProperties wp ON w2.WidgetID = wp.WidgetID AND wp.PropertyName = 'b'
ORDER BY w1.WidgetID


--q2
SELECT w1.WidgetID, w2.SomeValue, wp.PropertyName
FROM #widgets1 w1
LEFT JOIN #widgets2 w2 --no ON clause here
JOIN #widgetProperties wp
 ON w2.WidgetID = wp.WidgetID AND wp.PropertyName = 'b'
 ON w2.WidgetID = w1.WidgetID
ORDER BY w1.WidgetID


--q3
SELECT w1.WidgetID, w2.SomeValue, wp.PropertyName
FROM #widgets1 w1
LEFT JOIN (
    #widgets2 w2 --no SELECT or FROM here
    JOIN #widgetProperties wp
    ON w2.WidgetID = wp.WidgetID AND wp.PropertyName = 'b')
ON w2.WidgetID = w1.WidgetID
ORDER BY w1.WidgetID

q1 parece normal. q2 e q3 têm esses posicionamentos incomuns da ONcláusula.

Este script não necessariamente faz muito sentido. Foi difícil para mim inventar um cenário significativo.

Então, o que esses padrões de sintaxe incomuns significam? Como isso é definido? Percebi que nem todas as posições e ordenações para as duas ONcláusulas são permitidas. Quais são as regras que regem isso?

Além disso, é sempre uma boa ideia escrever consultas como esta?

sql-server t-sql
  • 2 respostas
  • 2509 Views
Martin Hope
Buzut
Asked: 2015-11-16 10:52:35 +0800 CST

Mecanismos de armazenamento MongoDB MMAPv1 vs WiredTiger

  • 26

No mongoDB3 apareceu um novo mecanismo de armazenamento: WiredTiger . No entanto, o MMAPv1 ainda é a opção padrão no Mongo .

Um pode não ser melhor que o outro, geralmente é uma questão de caso de uso e de escolher a ferramenta certa para o trabalho. Mas qual motor é o certo para qual trabalho?

Na verdade, enquanto o MMAPv1 é o mecanismo padrão, o WiredTiger parece melhor em quase todos os campos. Possui os mesmos recursos do MMAPv1 mais:

  • melhor desempenho de gravação,
  • simultaneidade em nível de documento,
  • compressão,
  • instantâneos e sistema de pontos de verificação.

Encontrei uma tabela comparativa no blog do MongoDB :

Comparação WiredTiger e MMAPv1

Então, exceto se você estiver no Solaris, há algum motivo para não escolher o WiredTiger?


EDITAR

Aqui estão dois vídeos que explicam em detalhes o funcionamento interno do WiredTiger e do MMAPv1 .

mongodb storage-engine
  • 3 respostas
  • 23615 Views
Martin Hope
Geoff Patterson
Asked: 2015-11-05 06:24:23 +0800 CST

Por que a função LEN() subestima mal a cardinalidade no SQL Server 2014?

  • 26

Eu tenho uma tabela com uma coluna de string e um predicado que verifica se há linhas com um determinado comprimento. No SQL Server 2014, vejo uma estimativa de 1 linha, independentemente do comprimento que estou verificando. Isso está gerando planos muito ruins porque, na verdade, existem milhares ou até milhões de linhas e o SQL Server está optando por colocar essa tabela no lado externo de um loop aninhado.

Existe uma explicação para a estimativa de cardinalidade de 1,0003 para o SQL Server 2014 enquanto o SQL Server 2012 estima 31.622 linhas? Existe uma boa solução alternativa?

Aqui está uma pequena reprodução do problema:

-- Create a table with 1MM rows of dummy data
CREATE TABLE #customers (cust_nbr VARCHAR(10) NOT NULL)
GO

INSERT INTO #customers WITH (TABLOCK) (cust_nbr)
    SELECT TOP 1000000 
        CONVERT(VARCHAR(10),
        ROW_NUMBER() OVER (ORDER BY (SELECT NULL))) AS cust_nbr
    FROM master..spt_values v1
    CROSS JOIN master..spt_values v2
GO

-- Looking for string of a certain length.
-- While both CEs yield fairly poor estimates, the 2012 CE is much
-- more conservative (higher estimate) and therefore much more likely
-- to yield an okay plan rather than a drastically understimated loop join.
-- 2012: 31,622 rows estimated, 900K rows actual
-- 2014: 1 row estimated, 900K rows actual
SELECT COUNT(*)
FROM #customers
WHERE LEN(cust_nbr) = 6
OPTION (QUERYTRACEON 9481) -- Optionally, use 2012 CE
GO

Aqui está um script mais completo mostrando testes adicionais

Também li o whitepaper sobre o Estimador de cardinalidade do SQL Server 2014 , mas não encontrei nada que esclarecesse a situação.

sql-server sql-server-2014
  • 2 respostas
  • 1506 Views
Martin Hope
Martin
Asked: 2015-10-17 10:28:27 +0800 CST

Como otimizar SELECT muito lento com LEFT JOINs em tabelas grandes

  • 26

Eu estava pesquisando, autodidata e procurando soluções por horas, mas sem sorte. Encontrei algumas perguntas semelhantes aqui, mas não este caso.

Minhas mesas:

  • pessoas (~ 10 milhões de linhas)
  • atributos (local, idade,...)
  • links (M:M) entre pessoas e atributos (~40 milhões de linhas)

Despejo completo ~ 280 MB

Situação: Procuro selecionar todos os ids de pessoa ( person_id) de algumas localidades ( location.attribute_value BETWEEN 3000 AND 7000), ser de algum gênero ( gender.attribute_value = 1), ter nascido em alguns anos ( bornyear.attribute_value BETWEEN 1980 AND 2000) e ter alguma cor dos olhos ( eyecolor.attribute_value IN (2,3)).

Esta é a minha consulta que leva de 3 a 4 minutos. e gostaria de otimizar:

SELECT person_id
FROM person
    LEFT JOIN attribute location ON location.attribute_type_id = 1 AND location.person_id = person.person_id
    LEFT JOIN attribute gender ON gender.attribute_type_id = 2 AND gender.person_id = person.person_id
    LEFT JOIN attribute bornyear ON bornyear.attribute_type_id = 3 AND bornyear.person_id = person.person_id
    LEFT JOIN attribute eyecolor ON eyecolor.attribute_type_id = 4 AND eyecolor.person_id = person.person_id
WHERE 1
    AND location.attribute_value BETWEEN 3000 AND 7000
    AND gender.attribute_value = 1
    AND bornyear.attribute_value BETWEEN 1980 AND 2000
    AND eyecolor.attribute_value IN (2,3)
LIMIT 100000;

Resultado:

+-----------+
| person_id |
+-----------+
|       233 |
|       605 |
|       ... |
|   8702599 |
|   8703617 |
+-----------+
100000 rows in set (3 min 42.77 sec)

Explique estendida:

+----+-------------+----------+--------+---------------------------------------------+-----------------+---------+--------------------------+---------+----------+--------------------------+
| id | select_type | table    | type   | possible_keys                               | key             | key_len | ref                      | rows    | filtered | Extra                    |
+----+-------------+----------+--------+---------------------------------------------+-----------------+---------+--------------------------+---------+----------+--------------------------+
|  1 | SIMPLE      | bornyear | range  | attribute_type_id,attribute_value,person_id | attribute_value | 5       | NULL                     | 1265229 |   100.00 | Using where              |
|  1 | SIMPLE      | location | ref    | attribute_type_id,attribute_value,person_id | person_id       | 5       | test1.bornyear.person_id |       4 |   100.00 | Using where              |
|  1 | SIMPLE      | eyecolor | ref    | attribute_type_id,attribute_value,person_id | person_id       | 5       | test1.bornyear.person_id |       4 |   100.00 | Using where              |
|  1 | SIMPLE      | gender   | ref    | attribute_type_id,attribute_value,person_id | person_id       | 5       | test1.eyecolor.person_id |       4 |   100.00 | Using where              |
|  1 | SIMPLE      | person   | eq_ref | PRIMARY                                     | PRIMARY         | 4       | test1.location.person_id |       1 |   100.00 | Using where; Using index |
+----+-------------+----------+--------+---------------------------------------------+-----------------+---------+--------------------------+---------+----------+--------------------------+
5 rows in set, 1 warning (0.02 sec)

Perfil:

+------------------------------+-----------+
| Status                       | Duration  |
+------------------------------+-----------+
| Sending data                 |  3.069452 |
| Waiting for query cache lock |  0.000017 |
| Sending data                 |  2.968915 |
| Waiting for query cache lock |  0.000019 |
| Sending data                 |  3.042468 |
| Waiting for query cache lock |  0.000043 |
| Sending data                 |  3.264984 |
| Waiting for query cache lock |  0.000017 |
| Sending data                 |  2.823919 |
| Waiting for query cache lock |  0.000038 |
| Sending data                 |  2.863903 |
| Waiting for query cache lock |  0.000014 |
| Sending data                 |  2.971079 |
| Waiting for query cache lock |  0.000020 |
| Sending data                 |  3.053197 |
| Waiting for query cache lock |  0.000087 |
| Sending data                 |  3.099053 |
| Waiting for query cache lock |  0.000035 |
| Sending data                 |  3.064186 |
| Waiting for query cache lock |  0.000017 |
| Sending data                 |  2.939404 |
| Waiting for query cache lock |  0.000018 |
| Sending data                 |  3.440288 |
| Waiting for query cache lock |  0.000086 |
| Sending data                 |  3.115798 |
| Waiting for query cache lock |  0.000068 |
| Sending data                 |  3.075427 |
| Waiting for query cache lock |  0.000072 |
| Sending data                 |  3.658319 |
| Waiting for query cache lock |  0.000061 |
| Sending data                 |  3.335427 |
| Waiting for query cache lock |  0.000049 |
| Sending data                 |  3.319430 |
| Waiting for query cache lock |  0.000061 |
| Sending data                 |  3.496563 |
| Waiting for query cache lock |  0.000029 |
| Sending data                 |  3.017041 |
| Waiting for query cache lock |  0.000032 |
| Sending data                 |  3.132841 |
| Waiting for query cache lock |  0.000050 |
| Sending data                 |  2.901310 |
| Waiting for query cache lock |  0.000016 |
| Sending data                 |  3.107269 |
| Waiting for query cache lock |  0.000062 |
| Sending data                 |  2.937373 |
| Waiting for query cache lock |  0.000016 |
| Sending data                 |  3.097082 |
| Waiting for query cache lock |  0.000261 |
| Sending data                 |  3.026108 |
| Waiting for query cache lock |  0.000026 |
| Sending data                 |  3.089760 |
| Waiting for query cache lock |  0.000041 |
| Sending data                 |  3.012763 |
| Waiting for query cache lock |  0.000021 |
| Sending data                 |  3.069694 |
| Waiting for query cache lock |  0.000046 |
| Sending data                 |  3.591908 |
| Waiting for query cache lock |  0.000060 |
| Sending data                 |  3.526693 |
| Waiting for query cache lock |  0.000076 |
| Sending data                 |  3.772659 |
| Waiting for query cache lock |  0.000069 |
| Sending data                 |  3.346089 |
| Waiting for query cache lock |  0.000245 |
| Sending data                 |  3.300460 |
| Waiting for query cache lock |  0.000019 |
| Sending data                 |  3.135361 |
| Waiting for query cache lock |  0.000021 |
| Sending data                 |  2.909447 |
| Waiting for query cache lock |  0.000039 |
| Sending data                 |  3.337561 |
| Waiting for query cache lock |  0.000140 |
| Sending data                 |  3.138180 |
| Waiting for query cache lock |  0.000090 |
| Sending data                 |  3.060687 |
| Waiting for query cache lock |  0.000085 |
| Sending data                 |  2.938677 |
| Waiting for query cache lock |  0.000041 |
| Sending data                 |  2.977974 |
| Waiting for query cache lock |  0.000872 |
| Sending data                 |  2.918640 |
| Waiting for query cache lock |  0.000036 |
| Sending data                 |  2.975842 |
| Waiting for query cache lock |  0.000051 |
| Sending data                 |  2.918988 |
| Waiting for query cache lock |  0.000021 |
| Sending data                 |  2.943810 |
| Waiting for query cache lock |  0.000061 |
| Sending data                 |  3.330211 |
| Waiting for query cache lock |  0.000025 |
| Sending data                 |  3.411236 |
| Waiting for query cache lock |  0.000023 |
| Sending data                 | 23.339035 |
| end                          |  0.000807 |
| query end                    |  0.000023 |
| closing tables               |  0.000325 |
| freeing items                |  0.001217 |
| logging slow query           |  0.000007 |
| logging slow query           |  0.000011 |
| cleaning up                  |  0.000104 |
+------------------------------+-----------+
100 rows in set (0.00 sec)

Estruturas de tabelas:

CREATE TABLE `attribute` (
  `attribute_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `attribute_type_id` int(11) unsigned DEFAULT NULL,
  `attribute_value` int(6) DEFAULT NULL,
  `person_id` int(11) unsigned DEFAULT NULL,
  PRIMARY KEY (`attribute_id`),
  KEY `attribute_type_id` (`attribute_type_id`),
  KEY `attribute_value` (`attribute_value`),
  KEY `person_id` (`person_id`)
) ENGINE=MyISAM AUTO_INCREMENT=40000001 DEFAULT CHARSET=utf8;

CREATE TABLE `person` (
  `person_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `person_name` text CHARACTER SET latin1,
  PRIMARY KEY (`person_id`)
) ENGINE=MyISAM AUTO_INCREMENT=20000001 DEFAULT CHARSET=utf8;

A consulta foi realizada no servidor virtual da DigitalOcean com SSD e 1GB de RAM.

Suponho que pode haver problema com o design do banco de dados. Você tem alguma sugestão para projetar melhor esta situação, por favor? Ou apenas para ajustar o select acima?

mysql performance
  • 5 respostas
  • 131071 Views
Martin Hope
Erik Bergstedt
Asked: 2015-07-01 03:16:42 +0800 CST

Desempenho do índice em ON versus WHERE

  • 26

Eu tenho duas mesas

@T1 TABLE
(
    Id INT,
    Date DATETIME
)

@T2 TABLE
(
    Id INT,
    Date DATETIME
)

Essas tabelas têm um índice não clusterizado em (Id, Data)

E eu junto essas mesas

SELECT *
FROM T1 AS t1
INNER JOIN T2 AS t2
ON 
    t1.Id = t2.Id
WHERE 
    t1.Date <= GETDATE()
    AND
    t2.Date <= GETDATE()

Isso também pode ser escrito como

SELECT *
FROM T1 AS t1
INNER JOIN T2 AS t2
ON 
    t1.Id = t2.Id
    AND
    t1.Date <= GETDATE()
    AND
    t2.Date <= GETDATE()

Minha pergunta é: qual dessas duas consultas oferece o melhor desempenho e por quê? Ou são iguais?

sql-server index
  • 6 respostas
  • 3179 Views
Martin Hope
James Z
Asked: 2015-06-16 20:52:43 +0800 CST

Problema de otimização com função definida pelo usuário

  • 26

Tenho um problema para entender por que o servidor SQL decide chamar a função definida pelo usuário para cada valor na tabela, embora apenas uma linha deva ser buscada. O SQL real é muito mais complexo, mas consegui reduzir o problema a isso:

select  
    S.GROUPCODE,
    H.ORDERCATEGORY
from    
    ORDERLINE L
    join ORDERHDR H on H.ORDERID = L.ORDERID
    join PRODUCT P  on P.PRODUCT = L.PRODUCT    
    cross apply dbo.GetGroupCode (P.FACTORY) S
where   
    L.ORDERNUMBER = 'XXX/YYY-123456' and
    L.RMPHASE = '0' and
    L.ORDERLINE = '01'

Para esta consulta, o SQL Server decide chamar a função GetGroupCode para cada valor único que existe na tabela PRODUCT, mesmo que a estimativa e o número real de linhas retornadas de ORDERLINE sejam 1 (é a chave primária):

Plano de consulta

Mesmo plano no explorador de planos mostrando as contagens de linhas:

Explorador de planos Tabelas:

ORDERLINE: 1.5M rows, primary key: ORDERNUMBER + ORDERLINE + RMPHASE (clustered)
ORDERHDR:  900k rows, primary key: ORDERID (clustered)
PRODUCT:   6655 rows, primary key: PRODUCT (clustered)

O índice que está sendo usado para a varredura é:

create unique nonclustered index PRODUCT_FACTORY on PRODUCT (PRODUCT, FACTORY)

A função é, na verdade, um pouco mais complexa, mas a mesma coisa acontece com uma função fictícia de várias instruções como esta:

create function GetGroupCode (@FACTORY varchar(4))
returns @t table(
    TYPE        varchar(8),
    GROUPCODE   varchar(30)
)
as begin
    insert into @t (TYPE, GROUPCODE) values ('XX', 'YY')
    return
end

Consegui "consertar" o desempenho forçando o servidor SQL a buscar o 1 produto principal, embora 1 seja o máximo que pode ser encontrado:

select  
    S.GROUPCODE,
    H.ORDERCAT
from    
    ORDERLINE L
    join ORDERHDR H
        on H.ORDERID = M.ORDERID
    cross apply (select top 1 P.FACTORY from PRODUCT P where P.PRODUCT = L.PRODUCT) P
    cross apply dbo.GetGroupCode (P.FACTORY) S
where   
    L.ORDERNUMBER = 'XXX/YYY-123456' and
    L.RMPHASE = '0' and
    L.ORDERLINE = '01'

Em seguida, a forma do plano também muda para algo que eu esperava que fosse originalmente:

Plano de consulta com top

Eu também pensei que o índice PRODUCT_FACTORY sendo menor que o índice clusterizado PRODUCT_PK teria um efeito, mas mesmo forçando a consulta a usar PRODUCT_PK, o plano ainda é o mesmo do original, com 6655 chamadas para a função.

Se eu deixar ORDERHDR completamente de fora, o plano começa com um loop aninhado entre ORDERLINE e PRODUCT primeiro, e a função é chamada apenas uma vez.

Gostaria de entender qual pode ser o motivo disso já que todas as operações são feitas usando chaves primárias e como corrigir caso aconteça em uma consulta mais complexa que não pode ser resolvida com tanta facilidade.

Editar: criar instruções de tabela:

CREATE TABLE dbo.ORDERHDR(
    ORDERID varchar(8) NOT NULL,
    ORDERCATEGORY varchar(2) NULL,
    CONSTRAINT ORDERHDR_PK PRIMARY KEY CLUSTERED (ORDERID)
)

CREATE TABLE dbo.ORDERLINE(
    ORDERNUMBER varchar(16) NOT NULL,
    RMPHASE char(1) NOT NULL,
    ORDERLINE char(2) NOT NULL,
    ORDERID varchar(8) NOT NULL,
    PRODUCT varchar(8) NOT NULL,
    CONSTRAINT ORDERLINE_PK PRIMARY KEY CLUSTERED (ORDERNUMBER,ORDERLINE,RMPHASE)
)

CREATE TABLE dbo.PRODUCT(
    PRODUCT varchar(8) NOT NULL,
    FACTORY varchar(4) NULL,
    CONSTRAINT PRODUCT_PK PRIMARY KEY CLUSTERED (PRODUCT)
)
sql-server sql-server-2005
  • 2 respostas
  • 5149 Views
Prev
Próximo

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