Ordinamento specificato nella chiave primaria, tuttavia l'ordinamento viene eseguito su SELECT


15

Sto memorizzando i dati del sensore in una tabella SensorValues . La tabella e la chiave primaria sono le seguenti:

CREATE TABLE [dbo].[SensorValues](
  [DeviceId] [int] NOT NULL,
  [SensorId] [int] NOT NULL,
  [SensorValue] [int] NOT NULL,
  [Date] [int] NOT NULL,
CONSTRAINT [PK_SensorValues] PRIMARY KEY CLUSTERED 
(
  [DeviceId] ASC,
  [SensorId] ASC,
  [Date] DESC
) WITH (
    FILLFACTOR=75,
    DATA_COMPRESSION = PAGE,
    PAD_INDEX = OFF,
    STATISTICS_NORECOMPUTE = OFF,
    SORT_IN_TEMPDB = OFF,
    IGNORE_DUP_KEY = OFF,
    ONLINE = OFF,
    ALLOW_ROW_LOCKS = ON,
    ALLOW_PAGE_LOCKS = ON)
  ON [MyPartitioningScheme]([Date])

Tuttavia, quando seleziono il valore del sensore valido per un tempo specifico, il piano di esecuzione mi dice che sta facendo una specie. Perché?

Avrei pensato che da quando ho archiviato i valori ordinati per colonna Data, l'ordinamento non si sarebbe verificato. O è perché l'indice non è ordinato esclusivamente dalla colonna Data, ovvero non può presumere che il set di risultati sia ordinato?

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010
  ORDER BY Date DESC

Progetto esecutivo

Modifica: posso farlo invece?

Dal momento che la tabella è ordinata DeviceId, SensorId, Date e I eseguiamo un SELECT che specifica solo un DeviceId e un SensorId , il set di output dovrebbe già essere ordinato per Data DESC . Quindi mi chiedo se la seguente domanda darebbe lo stesso risultato in tutti i casi?

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010

Secondo @Catcall di seguito, l'ordinamento non è uguale all'ordine di archiviazione. Cioè non possiamo presumere che i valori restituiti siano già in un ordine ordinato.

Modifica: ho provato questa soluzione CROSS APPLY, senza fortuna

@Martin Smith mi ha suggerito di provare ad applicare ESTERNO il mio risultato contro le partizioni. Ho trovato un post sul blog ( indici allineati non cluster sulla tabella partizionata ) che descrive questo problema simile e ho provato la soluzione in qualche modo simile a quella suggerita da Smith. Tuttavia, nessuna fortuna qui, i tempi di esecuzione sono alla pari con la mia soluzione originale.

WITH Boundaries(boundary_id)
AS
(
  SELECT boundary_id
  FROM sys.partition_functions pf
  JOIN sys.partition_range_values prf ON pf.function_id = prf.function_id
  WHERE pf.name = 'PF'
  AND prf.value <= 1339225010
  UNION ALL
  SELECT max(boundary_id) + 1
  FROM sys.partition_functions pf
  JOIN sys.partition_range_values prf ON pf.function_id = prf.function_id
  WHERE pf.name = 'PF'
  AND prf.value <= 1339225010
),
Top1(SensorValue)
AS
(
  SELECT TOP 1 d.SensorValue
  FROM Boundaries b
  CROSS APPLY
  (
    SELECT TOP 1 SensorValue
      FROM SensorValues
      WHERE  SensorId = 53
        AND DeviceId = 3819
        AND "Date" < 1339225010
        AND $Partition.PF(Date) = b.boundary_id
        ORDER BY Date DESC
  ) d
  ORDER BY d.Date DESC
)
SELECT SensorValue
FROM Top1

OPZIONE MAXDOP 1 non aiuta. Come specificato da @Martin Smith di seguito, sembra che il partizionamento sia ciò che lo sta causando ...
m__

Risposte:


13

Per una tabella non partizionata ottengo il seguente piano

Piano 1

C'è un unico predicato di ricerca su Seek Keys[1]: Prefix: DeviceId, SensorId = (3819, 53), Start: Date < 1339225010.

Ciò significa che SQL Server può eseguire una ricerca di uguaglianza sulle prime due colonne e quindi iniziare una ricerca di intervallo a partire da 1339225010e ordinata FORWARD(come definito dall'indice [Date] DESC)

L' TOPoperatore smetterà di richiedere più righe dalla ricerca dopo l'emissione della prima riga.

Quando creo lo schema e la funzione di partizione

CREATE PARTITION FUNCTION PF (int)
AS RANGE LEFT FOR VALUES (1000, 1339225009 ,1339225010 , 1339225011);
GO
CREATE PARTITION SCHEME [MyPartitioningScheme]
AS PARTITION PF
ALL TO ([PRIMARY] );

E popolare la tabella con i seguenti dati

INSERT INTO [dbo].[SensorValues]    
/*500 rows matching date and SensorId, DeviceId predicate*/
SELECT TOP (500) 3819,53,1, ROW_NUMBER() OVER (ORDER BY (SELECT 0))           
FROM master..spt_values
UNION ALL
/*700 rows matching date but not SensorId, DeviceId predicate*/
SELECT TOP (700) 3819,52,1, ROW_NUMBER() OVER (ORDER BY (SELECT 0))           
FROM master..spt_values
UNION ALL 
/*1100 rows matching SensorId, DeviceId predicate but not date */
SELECT TOP (1100) 3819,53,1, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) + 1339225011      
FROM master..spt_values

Il piano su SQL Server 2008 è il seguente.

Piano 2

Il numero effettivo di righe emesse dalla ricerca è 500. Il piano mostra cercare predicati

Seek Keys[1]: Start: PtnId1000 <= 2, End: PtnId1000 >= 1, 
Seek Keys[2]: Prefix: DeviceId, SensorId = (3819, 53), Start: Date < 1339225010

Indicare che sta usando l' approccio skip scan descritto qui

Query Optimizer è esteso in modo che un'operazione di ricerca o scansione con una condizione possa essere eseguita su PartitionID (come colonna iniziale logica) e possibilmente altre colonne chiave di indice, quindi è possibile eseguire una ricerca di secondo livello, con una condizione diversa su una o più colonne aggiuntive, per ogni valore distinto che soddisfa la qualifica per l'operazione di ricerca di primo livello.

Questo piano è un piano seriale e quindi per la specifica query che hai sembra che se SQL Server si è assicurato che elaborasse le partizioni in ordine decrescente di datequello che il piano originale TOPavrebbe ancora funzionato e avrebbe potuto interrompere l'elaborazione dopo che la prima riga corrispondente era trovato piuttosto che continuare e produrre le restanti 499 partite.

In effetti, il piano per il 2005 sembra adottare questo approccio

Pianificare il 2005

Non sono sicuro se è dritto in avanti per ottenere lo stesso piano rispetto al 2008 o forse avrebbe bisogno di un OUTER APPLYa sys.partition_range_valuessimulare.



9

Molte persone credono che un indice cluster garantisca un ordinamento sull'output. Ma non è quello che fa; garantisce un ordine di archiviazione su disco.

Vedi, ad esempio, questo post sul blog e questa discussione più lunga .


1
Bene, in precedenza, l'OP ha anche detto: "Avrei pensato che da quando ho archiviato i valori ordinati per colonna Date, l'ordinamento non si sarebbe verificato [sic]". Quindi almeno una parte del problema è quel malinteso su cosa fa un indice cluster. Penso che sia bene raddrizzarlo comunque.
Mike Sherrill "Cat Recall",

Forse sono solo testardo (quindi per favore perdonami ;-)). Comunque, ho letto il post sul blog di Hugo Kornelis ed è piuttosto semplice. Tuttavia, nel suo esempio sta usando un indice cluster e uno non cluster, l'indice non cluster ha dimensioni inferiori e viene quindi utilizzato nel piano di esecuzione. Nel mio caso ho un solo indice cluster, il server sql può ancora restituire i valori in ordine errato (non ha un indice più piccolo da usare e le scansioni complete delle tabelle sono troppo lente)?
m__

L'ho spostato in una nuova domanda (fuori tema)
m__

5

Sto ipotizzando che il SORT sia necessario a causa del piano parallelo. Baso questo su un articolo di blog debole e distante: ma l'ho trovato su MSDN che potrebbe o meno giustificarlo

Quindi, prova con MAXDOP 1 e vedi cosa succede ...

Anche accennato nel post del blog di kiwi @sql sulla semplice conversazione in "Cambio operatore" penso. E "dipendenza DOP" qui


Anche se non mi ero mai preoccupato di impostare una funzione di partizione dateprima. Ora ho e sembra essere il partizionamento è il colpevole con il 2005 forse comportarsi meglio per questa particolare query.
Martin Smith,

1

Fondamentalmente hai ragione - poiché la chiave primaria è nell'ordine "DeviceId, SensorId, Date", i dati nella chiave non sono ordinati per data, quindi non possono essere utilizzati. Se la chiave fosse in un ordine diverso "Data, DeviceId, SensorId", i dati nella chiave sarebbero ordinati per data, quindi potrebbero essere utilizzati ...


Avevo già provato a cambiare la chiave come hai menzionato, quindi non dispiacerti. Ad ogni modo, proverò a creare l'indice non cluster su tutte e 3 le colonne e vedere cosa mi dà. (la ricerca dell'indice mancante continua ... ;-))
m__
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.