Ajuste de desempenho para os drivers Microsoft para PHP para SQL Server

Descarregar o driver PHP

Este artigo aborda como escrever código PHP rápido contra SQL Server, Base de Dados SQL do Azure, Azure SQL Managed Instance, Azure Synapse Analytics e base de dados SQL em Microsoft Fabric. A orientação aplica-se tanto ao SQLSRV como ao PDO_SQLSRV, que envolvem o mesmo Microsoft ODBC Driver subjacente para SQL Server.

Comece pelas mudanças de maior impacto

Se só conseguir fazer três alterações, faça estas alterações:

  • Ativar o pooling de ligações. Estabelecer uma nova ligação TLS ao SQL Server demora dezenas a centenas de milissegundos, dependendo do caminho da rede e da negociação do TLS. Reutilizar ligações agrupadas elimina esse custo por pedido. Veja Gerir ligações de forma eficiente.
  • Busca apenas as colunas e linhas que precisas. SELECT * e as consultas ilimitadas são as causas mais comuns de endpoints lentos. Consulte Consulta apenas o que precisa.
  • Use parâmetros de tabela para inserções em massa. Para centenas de linhas ou mais, os parâmetros de tabela (TVPs) são tipicamente muito mais rápidos do que as instruções linha a INSERT linha e escalam linearmente com a contagem de linhas. Ver Inserir dados de forma eficiente.

Gerir as ligações de forma eficiente

O estabelecimento de ligação é a operação mais dispendiosa que o condutor realiza. Quase todas as investigações de desempenho do PHP terminam com uma correção de gestão de ligações.

Permitir o agrupamento de ligações

O pooling reutiliza ligações ODBC entre pedidos PHP em vez de as desmontar no final do pedido. O objeto de ligação é descartado quando o seu script termina, mas o handle ODBC subjacente mantém-se ativo no pool do gestor de drivers ODBC e é reutilizado pelo próximo pedido que pede a mesma cadeia de ligação.

Windows: O pooling de ligações está ativado por defeito. Para confirmar, deixa essa ConnectionPooling opção fora do teu DSN. Para desativar o pooling para depuração, defina ConnectionPooling=0.

Linux e macOS: O pooling de ligações não é uma opção DSN nestas plataformas. Ative-o no gestor de drivers definindo Pooling=Yes a [ODBC] secção de odbcinst.ini, e defina um positivo CPTimeout sob a estrofe do driver. Por exemplo:

[ODBC]
Pooling=Yes

[ODBC Driver 18 for SQL Server]
Description=Microsoft ODBC Driver 18 for SQL Server
Driver=/opt/microsoft/msodbcsql18/lib64/libmsodbcsql-18.<version>.so.1.1
CPTimeout=120

Encontre o caminho real da biblioteca com odbcinst -q -d -n "ODBC Driver 18 for SQL Server" ou ls /opt/microsoft/msodbcsql18/lib64/. O nome do ficheiro incorpora a versão instalada do driver ODBC e muda com cada lançamento.

CPTimeout (em segundos) controla quanto tempo as ligações de repouso ficam na piscina antes de serem fechadas. Define-a suficientemente alta para que a maioria dos pedidos encontre uma ligação em pool, mas baixa o suficiente para que as ligações obsoletas a um servidor com falha sejam desativadas razoavelmente depressa. 60 a 300 segundos funcionam bem para a maioria das cargas de trabalho web.

Para mais detalhes, consulte agrupamento de ligações.

Compreenda o custo da primeira consulta

O Multiple Active Result Sets (MARS) está ativado por defeito. Quando o MARS e o pooling de ligações estão ambos ativos, o driver reinicia a ligação em pool na primeira consulta, e esse reset ignora qualquer timeout da consulta que tenha definido para essa primeira consulta. As consultas posteriores na mesma ligação normalmente respeitam o timeout. Se definir tempos de espera agressivos na primeira consulta numa carga de trabalho agrupada, tenha em conta este comportamento, ou desative o MARS MultipleActiveResultSets=false se não precisar. Veja a nota MARS e pooling em Connection pooling.

Ligações PDO persistentes não são suportadas

PDO_SQLSRV rejeita PDO::ATTR_PERSISTENT. Definir nos lançamentos do construtor:

SQLSTATE[IMSSP]: An unsupported attribute was designated on the PDO object.

Use pooling de ligações ODBC para reutilização entre pedidos. É o mecanismo nativo do driver, funciona tanto para PDO_SQLSRV como para SQLSRV, e desativa ligações CPTimeout inativas (o que também mantém Microsoft Entra atualização de tokens honesta).

Reutilizar a ligação dentro de um pedido

Mesmo com pooling, abrir uma nova ligação PDO ou SQLSRV implica uma ida e volta ODBC para buscar e validar um handle em pool. Abre uma ligação uma vez por pedido e passa-a para todas as funções que precisarem.

Sugestão

Um contentor de injeção de dependência ou um acessório preguiçoso é suficiente. O objetivo é evitar new PDO(...) no meio de um gestor de pedidos.

Consulta apenas o que precisas

As viagens de rede e a materialização do conjunto de resultados dominam a latência de consulta na maioria das cargas de trabalho PHP. As correções são as mesmas que se aplicam a todas as camadas de acesso à base de dados.

Selecione apenas as colunas que utiliza

SELECT * Puxa todas as colunas, incluindo varchar(max) e varbinary(max) que eclipsam os dados que realmente consomes. Nomeie as colunas:

<?php
// Slow: fetches all columns, including a 2 MB LOB column
$stmt = $conn->query("SELECT * FROM dbo.Products");

// Fast: fetches only the two columns the caller uses
$stmt = $conn->query("SELECT ProductID, Name FROM dbo.Products");

Vai buscar apenas as filas de que precisas

Push filtering para o SQL Server. Nunca recolhas uma tabela completa no PHP só para filtrar num foreach loop.

<?php
// Slow: transfers every row to PHP, then filters
$rows = $conn->query("SELECT * FROM dbo.Orders")->fetchAll(PDO::FETCH_ASSOC);
$recent = array_filter($rows, fn($r) => $r["OrderDate"] > "2026-01-01");

// Fast: filters on the server
$stmt = $conn->prepare("SELECT OrderID, CustomerID, Total FROM dbo.Orders WHERE OrderDate > ?");
$stmt->execute(["2026-01-01"]);
$recent = $stmt->fetchAll(PDO::FETCH_ASSOC);

Paginar grandes conjuntos de resultados

Para uma vista de lista que mostra algumas centenas de linhas entre milhões, não devolvas todas as linhas e deixa o cliente tratar disso. Use paginação do lado do servidor com OFFSET ... FETCH:

<?php
function fetchPage(PDO $conn, int $page, int $pageSize): array {
    $stmt = $conn->prepare(
        "SELECT OrderID, CustomerID, Total
         FROM dbo.Orders
         ORDER BY OrderID
         OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"
    );
    // With native prepares, execute([...]) binds values as strings.
    // OFFSET and FETCH NEXT require integer bindings; bind explicitly.
    $stmt->bindValue(1, ($page - 1) * $pageSize, PDO::PARAM_INT);
    $stmt->bindValue(2, $pageSize, PDO::PARAM_INT);
    $stmt->execute();
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

Escolha o método certo de busca

  • Usa fetch(PDO::FETCH_ASSOC) em loop para iteração em streaming quando não precisas de todas as linhas na memória ao mesmo tempo.
  • fetchAll(PDO::FETCH_ASSOC) Use quando o chamador realmente precisa de todo o conjunto (por exemplo, ao renderizar uma resposta JSON completa).
  • Use fetchColumn() quando só se preocupa com um único escalar (um COUNT, SUM, ou MAX).
  • Use PDO::FETCH_KEY_PAIR ou PDO::FETCH_UNIQUE construa dicionários de consulta sem necessidade de segunda passagem.

Os modos numéricos de busca (PDO::FETCH_NUM) são marginalmente mais rápidos do que os modos associativos porque saltam a construção do mapa de nomes de coluna. Prefiro clareza; Só muda quando um perfilador sinaliza o overhead de obtenção como significativo.

Prefere SET NOCOUNT ON em procedimentos armazenados e lotes

Cada INSERTinstrução , UPDATE, and DELETE devolve um DONE_IN_PROC token com a contagem de linhas afetada, que o PHP normalmente descarta. O token não adiciona uma viagem de ida e volta, mas cada um ainda custa bytes no fio e uma pequena quantidade de trabalho de driver. Num procedimento ou lote de múltiplas instruções que executa centenas de extratos por chamada, as poupanças somam-se. Desliga-o:

CREATE OR ALTER PROCEDURE dbo.ProcessOrder
    @OrderID INT
AS
BEGIN
    SET NOCOUNT ON;

    UPDATE dbo.Inventory SET Stock = Stock - 1 WHERE ProductID IN (SELECT ProductID FROM dbo.OrderLines WHERE OrderID = @OrderID);
    UPDATE dbo.Orders SET Status = 'Processed' WHERE OrderID = @OrderID;
END;

Inserir dados de forma eficiente

Escolhe o método certo de inserção com base no número de carreiras que estás a mover. A escolha errada pode ser 100 vezes mais lenta.

Menos de cerca de 100 linhas: declaração preparada num ciclo

Para pequenos lotes, execute uma única instrução preparada num ciclo:

<?php
$stmt = $conn->prepare("INSERT INTO dbo.Products (Name, Price) VALUES (?, ?)");
foreach ($products as $p) {
    $stmt->execute([$p["name"], $p["price"]]);
}

Envolve o loop numa transação para que todos os inserts sejam comprometidos como uma unidade e o log não tenha de limpar após cada linha:

<?php
$conn->beginTransaction();
try {
    $stmt = $conn->prepare("INSERT INTO dbo.Products (Name, Price) VALUES (?, ?)");
    foreach ($products as $p) {
        $stmt->execute([$p["name"], $p["price"]]);
    }
    $conn->commit();
} catch (PDOException $e) {
    $conn->rollBack();
    throw $e;
}

Centenas a milhões de linhas: parâmetros com valores em tabela

Os parâmetros de tabela (TVPs) enviam todo o lote para o SQL Server numa só ida e volta e permitem que o SQL Server processe o conjunto como uma única instrução. Para lotes de centenas de linhas ou mais, os TVPs são tipicamente muito mais rápidos do que um ciclo de declaração preparada e escalam linearmente com a contagem de linhas.

Primeiro, crie um tipo de tabela no servidor:

CREATE TYPE dbo.ProductTableType AS TABLE (
    Name  NVARCHAR(100),
    Price DECIMAL(10, 2)
);

PDO_SQLSRV passa o TVP como um array associativo cuja chave é o nome do tipo e cujo valor é o conjunto de linhas. Vincule-o com PDO::PARAM_LOB:

<?php
$rows = [];
foreach ($products as $p) {
    $rows[] = [$p["name"], $p["price"]];
}
$tvpInput = ["ProductTableType" => $rows];

$stmt = $conn->prepare(
    "INSERT INTO dbo.Products (Name, Price) SELECT Name, Price FROM ?"
);
$stmt->bindParam(1, $tvpInput, PDO::PARAM_LOB);
$stmt->execute();

Para um esquema não padrão, passe o esquema como o próximo elemento do array: ["ProductTableType" => $rows, "Sales"]. Para exemplos de sintaxe procedural SQLSRV e procedimentos armazenados, veja Usar parâmetros com valores de tabela.

Milhões de linhas: bcp ou BULK INSERT

Para operações verdadeiramente em massa (cargas de data warehouse, migrações iniciais), use BCP ou BULK INSERT em vez de PHP. Escreve os teus dados num ficheiro delimitado ou de formato nativo, depois executa o bcp ou BULK INSERT a partir de um job agendado, passo ETL ou script de administração.

Caution

Se pagares para BCP a partir do PHP com shell_exec() ou proc_open(), nunca interpoles entradas não confiáveis na linha de comandos. Use escapeshellarg() em todos os argumentos, e prefira executar o carregamento fora de banda em vez de num caminho de pedido web.

Reduzir viagens de ida e volta

Cada viagem de ida e volta em rede entre PHP e SQL Server tem um custo fixo. Quando envia cinco extratos num só lote, paga esse custo uma vez em vez de cinco.

Para trabalhos relacionados que correm em conjunto, coloque as instruções num só lote e consuma todos os conjuntos de resultados:

<?php
$sql = "
    SELECT * FROM dbo.Customers WHERE CustomerID = ?;
    SELECT * FROM dbo.Orders WHERE CustomerID = ?;
    SELECT * FROM dbo.Addresses WHERE CustomerID = ?;
";
$stmt = $conn->prepare($sql);
$stmt->execute([$id, $id, $id]);

$customer = $stmt->fetch(PDO::FETCH_ASSOC);

$stmt->nextRowset();
$orders = $stmt->fetchAll(PDO::FETCH_ASSOC);

$stmt->nextRowset();
$addresses = $stmt->fetchAll(PDO::FETCH_ASSOC);

Para SQLSRV, use sqlsrv_next_result para avançar entre conjuntos de resultados.

Ative Múltiplos Conjuntos de Resultados Ativos quando precisar

Múltiplos Conjuntos de Resultados Ativos (MARS) permitem que uma única ligação tenha múltiplas instruções ativas. Sem o MARS, não pode emitir uma nova consulta numa ligação que ainda tenha um conjunto de resultados aberto. Ambos os drivers ativam o MARS por defeito. Para desligar, define MultipleActiveResultSets=false a tua cadeia de ligação. Ver Desativar Múltiplos Conjuntos de Resultados Ativos (MARS).

O MARS é conveniente, mas não gratuito. Cada conjunto de resultados ativo consome recursos do lado do servidor. Prefira consumir um conjunto de resultados completo antes de começar outro. Usa o MARS para desbloquear padrões de cursor genuinamente aninhados.

Afinar declarações preparadas

As instruções preparadas salvam o driver de voltar a analisar SQL no servidor e permitem-te associar com segurança entradas não confiáveis como parâmetros.

Prefira preparações nativas

PDO_SQLSRV pode preparar instruções em dois modos. O Native Prepara envia o texto SQL para o servidor uma vez e reutiliza a instrução analisada para cada execução, enviando apenas os valores dos parâmetros em cada execute(). As preparações emuladas mantêm o texto SQL no cliente e reconstroem uma string SQL completa com parâmetros interpolados em cada execução.

Define PDO::ATTR_EMULATE_PREPARES => false para que o driver use preparações nativas. As preparações nativas permitem que o SQL Server armazene em cache e reutilize o plano de consulta, e evitam re-analisar o texto SQL em cada execução.

<?php
$conn = new PDO($dsn, null, null, [
    PDO::ATTR_ERRMODE          => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_EMULATE_PREPARES => false,
]);

Reutilizar declarações preparadas

Preparar uma vez, executar muitos. Cada prepare() chamada custa uma alocação de handle ODBC e uma análise do lado do servidor. Num circuito quente, mantenha o $stmt objeto vivo e chame execute() dentro do ciclo:

<?php
// Fast: one prepare, many executes.
$stmt = $conn->prepare("UPDATE dbo.Inventory SET Stock = Stock - ? WHERE ProductID = ?");
foreach ($orderLines as $line) {
    $stmt->execute([$line["qty"], $line["productId"]]);
}

// Slow: re-prepares the same SQL on every iteration.
foreach ($orderLines as $line) {
    $stmt = $conn->prepare("UPDATE dbo.Inventory SET Stock = Stock - ? WHERE ProductID = ?");
    $stmt->execute([$line["qty"], $line["productId"]]);
}

Cuidado com TOP (?) e IN (?, ?, ...)

TOPrequer parênteses em torno de um marcador de parâmetro, SELECT TOP (?) ..., para que o SQL Server possa analisar a contagem de linhas como parâmetro. IN (?, ?, ?, ?) requer uma contagem de substituição fixa no momento da preparação. Para tamanhos dinâmicos IN de lista, constrói a cadeia provisória a partir de uma contagem de inteiros validada, ou passa a lista como um parâmetro com valor em tabela.

Caution

Nunca interpoles a entrada bruta do utilizador no texto SQL (incluindo a contagem de placeholder). Lança a contagem com (int) antes de construir a cadeia provisória e passa sempre os valores execute() reais como parâmetros.

Gerir cursores e memória

O tipo de cursor padrão é PDO::CURSOR_FWDONLY, uma mangueira de incêndio apenas para avançar. Transmite linhas para PHP uma de cada vez e não faz buffer, por isso um grande conjunto de resultados é limitado pela memória de buffer de linhas em vez da contagem total de linhas. Normalmente é isso que queres.

Usa cursores com buffer apenas quando precisares de recuar ou contar linhas

PDO::SQLSRV_CURSOR_BUFFERED (um cursor estático com buffer do lado do cliente) recolhe todo o conjunto de resultados para a memória PHP imediatamente. Esta abordagem permite-lhe chamar rowCount(), procurar para trás e reutilizar a afirmação. Por defeito, o buffer está limitado a 10.240 KB (10 MB) via PDO::SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE, e uma consulta cujo conjunto de resultados excede os retornos false do limite, em vez de sobrecarregar memória PHP. Podes aumentar o limite até ao limite de memória do PHP, mas ao fazê-lo trocas um false retorno por um erro fatal real Allowed memory size exhausted quando uma consulta ultrapassa o novo limite. Afina deliberadamente. Ver Tipos de cursor (PDO_SQLSRV).

Os cursores scrolláveis do lado do servidor (PDO::SQLSRV_CURSOR_STATIC, PDO::SQLSRV_CURSOR_DYNAMIC, PDO::SQLSRV_CURSOR_KEYSET) fazem buffer no servidor em vez do cliente, para não consomem memória PHP. No entanto, armazenam recursos do lado do servidor durante toda a duração do cursor e são mais lentos por linha do que apenas para avançar.

Use o modo predefinido de apenas para frente para leituras em streaming. Usa buffer do lado do cliente para conjuntos de resultados pequenos quando precisares rowCount() ou scroll para trás. Evita cursores scrolláveis do lado do servidor, a menos que estejas a fazer algo específico.

<?php
// Fast, low memory: default forward-only, one row at a time
$stmt = $conn->prepare("SELECT OrderID, Total FROM dbo.Orders");
$stmt->execute();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    // ...
}

// Buffered: only when you need rowCount() or seeking
$stmt = $conn->prepare("SELECT * FROM dbo.SmallLookup", [
    PDO::ATTR_CURSOR                    => PDO::CURSOR_SCROLL,
    PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => PDO::SQLSRV_CURSOR_BUFFERED,
]);
$stmt->execute();
$rowCount = $stmt->rowCount();

Para uma explicação completa, veja Tipos de cursor (PDO_SQLSRV) e Tipos de cursor (SQLSRV).

Fluxar valores binários grandes e de caracteres

Para varbinary(max), varchar(max), nvarchar(max), xml e outros tipos grandes, use fluxos PHP em vez de materializar o valor total na memória:

<?php
$stmt = $conn->prepare("SELECT Name, PhotoBlob FROM dbo.Products WHERE ProductID = ?");
$stmt->execute([$id]);
$stmt->bindColumn("PhotoBlob", $photo, PDO::PARAM_LOB);
$stmt->fetch(PDO::FETCH_BOUND);

// $photo is a stream resource; write it directly to disk without loading it all
$outFile = fopen("/tmp/photo.bin", "wb");
stream_copy_to_stream($photo, $outFile);
fclose($outFile);

Para inserir ou atualizar com valores grandes, use SendStreamParamsAtExec=false no SQLSRV para enviar dados de fluxo em blocos após sqlsrv_execute(). Para mais detalhes, veja Enviar dados como fluxo.

Defina os tempos limite apropriados

Os timeouts são tanto definições de desempenho como de fiabilidade. Perguntas longas mantêm ligações à piscina e impedem outros pedidos.

Tempo limite da instrução

Defina um limite de tempo por instrução para que uma consulta descontrolada não retenha uma ligação ao pool indefinidamente. Para PDO_SQLSRV:

<?php
$stmt = $conn->prepare("SELECT ... FROM dbo.HugeTable ...");
$stmt->setAttribute(PDO::SQLSRV_ATTR_QUERY_TIMEOUT, 30); // seconds
$stmt->execute();

Para SQLSRV, passe "QueryTimeout" => 30 o array de opções para sqlsrv_query ou sqlsrv_prepare.

Define um valor que corresponda à tua carga de trabalho. Para um pedido web síncrono, 15 a 30 segundos é o normal. Para um trabalho em lote de fundo, vários minutos podem ser razoáveis. Nunca definas o timeout para zero (ilimitado) num pedido web.

Tempo limite de login

LoginTimeoutna cadeia de ligação controla quanto tempo o driver espera para estabelecer uma ligação. Defina um valor explícito ao ligar ao Base de Dados SQL do Azure ou ao Azure SQL Managed Instance para que arranques a frio e failovers em grupos de failover não bloqueiem o cliente indefinidamente. Valores entre 30 e 90 segundos funcionam bem para a maioria das cargas de trabalho na cloud. Para detalhes sobre dimensionar LoginTimeout contra ConnectRetryCount * ConnectRetryInterval e os modos de falha resultantes, veja Tempo de Expiração da Ligação. Para a referência de opções, veja Opções de Ligação.

Encaminhar cargas de trabalho apenas de leitura para uma réplica

Para consultas apenas de leitura contra uma base de dados num grupo de disponibilidade Always On, Azure SQL Managed Instance, ou Base de Dados SQL do Azure com escala de leitura ou geo-réplica, adicione ApplicationIntent=ReadOnly à sua cadeia de ligação:

<?php
$dsn = "sqlsrv:Server=<listener>;Database=<database>;" .
       "Encrypt=true;ApplicationIntent=ReadOnly";

O encaminhamento só de leitura envia a ligação para uma réplica secundária sincronizada, descarregando trabalho do primário. Combine com MultiSubnetFailover=true para obter a ligação mais rápida a ouvintes de grupos de disponibilidade multi-sub-net.

Observe o desempenho do servidor

O timing do lado do cliente só indica quanto tempo uma consulta demorou de ponta a ponta. Para perceberes porque é que estava lento, usa os diagnósticos integrados do SQL Server.

Query Store

A Query Store captura planos de execução, estatísticas de execução e estatísticas de espera para cada consulta na base de dados. Está ativado por defeito no Base de Dados SQL do Azure, Azure SQL Managed Instance e SQL Database no Fabric. No SQL Server, ative-o por base de dados:

ALTER DATABASE <database_name> SET QUERY_STORE = ON;

Depois, use os relatórios Query Store do SQL Server Management Studio para encontrar as suas consultas mais lentas e frequentemente executadas. Veja Monitorização de desempenho com a Query Store.

SQL do Azure Query Performance Insight

Para o Base de Dados SQL do Azure, o Query Performance Insight do portal Azure mostra automaticamente as consultas que mais consomem recursos sem qualquer configuração. Para obter mais informações, veja Query Performance Insight para a Base de Dados SQL do Azure.

SET STATISTICS para uma investigação única

Para uma única consulta que queira perfilar, execute-a no SQL Server Management Studio com estatísticas ativadas:

SET STATISTICS TIME ON;
SET STATISTICS IO ON;

-- your query here

Leituras lógicas elevadas quase sempre significam um índice em falta ou inutilizável. Tempo de CPU elevado com leituras lógicas baixas geralmente significa um plano errado (sniffing de parâmetros, uma conversão implícita que impede o uso de índices, ou uma função escalar que previne o paralelismo).

Eventos Estendidos para rastreamento ao nível do condutor

Para ver exatamente o que o driver envia para o SQL Server (incluindo os valores reais dos parâmetros que interpola), capture uma sessão de Eventos Estendidos usando os rpc_completed eventos e sql_batch_completed .

Performance checklist (Lista de verificação do desempenho)

Use esta lista de verificação como uma revisão pré-implementação de qualquer aplicação PHP que se ligue ao SQL Server:

Área Verificar Reference
Connection O pooling de ligações está ativado e configurado para a plataforma Gerir as ligações de forma eficiente
Connection A aplicação reutiliza ligações dentro de um pedido e não abre ligações por consulta Reutilizar a ligação dentro de um pedido
Connection LoginTimeoutcobre arranques a frio e failover para SQL do Azure Tempo limite de início de sessão
Query As consultas selecionam apenas as colunas necessárias, não SELECT * Selecione apenas as colunas que utiliza
Query A filtragem acontece em SQL, não em PHP com array_filter Vai buscar apenas as filas de que precisas
Query Grandes conjuntos de resultados são paginados com OFFSET ... FETCH Paginar grandes conjuntos de resultados
Query Conjunto de procedimentos armazenados SET NOCOUNT ON Prefere SET NOCOUNT ON
Inserções As inserções em massa usam parâmetros de valor de tabela, não laços por linha Inserir dados de forma eficiente
Statements PDO::ATTR_EMULATE_PREPARES está configurado para false Prefira preparações nativas
Statements A aplicação reutiliza instruções preparadas entre execuções Reutilizar declarações preparadas
Cursors A aplicação utiliza o cursor predefinido apenas para frente, a menos que seja necessário buffering Gerir cursores e memória
Memory Grandes valores binários e de caracteres são transmitidos em streaming, não materializados Fluxar valores binários grandes e de caracteres
Interrupções O timeout das instruções é definido em todas as consultas direcionadas ao utilizador Tempo limite da extração
Roteamento Cargas de trabalho apenas de leitura definidas ApplicationIntent=ReadOnly onde existe uma réplica Cargas de trabalho apenas de leitura de rotas
Observability A Query Store está ativada e é revista regularmente Query Store