ist von einem Typ, der für die Verwendung als Schlüsselspalte in einem Index ungültig ist

178

Ich habe einen Fehler bei

Column 'key' in table 'misc_info' is of a type that is invalid for use as a key column in an index.

Dabei ist der Schlüssel ein nvarchar (max). Eine schnelle Google fand dies . Es erklärt jedoch nicht, was eine Lösung ist. Wie erstelle ich so etwas wie ein Wörterbuch, bei dem der Schlüssel und der Wert beide Zeichenfolgen sind und der Schlüssel offensichtlich eindeutig und einfach sein muss? Meine SQL-Anweisung war

create table [misc_info] (
[id] INTEGER PRIMARY KEY IDENTITY NOT NULL,
[key] nvarchar(max) UNIQUE NOT NULL,
[value] nvarchar(max) NOT NULL);

quelle
16
Benötigen Sie wirklich, dass Ihr Schlüssel (möglicherweise) 4 GB groß UND einzigartig ist? SqlServer lässt dies nicht zu, da das Überprüfen der Eindeutigkeit möglicherweise sehr zeitaufwändig sein kann.
Klaus Byskov Pedersen
@KlausByskovPedersen Einige leistungsfähigere DBMS wie PostgreSQL sind intelligent genug, um dies zuzulassen und stattdessen einen Digest zu indizieren. Aber du hast einen Punkt.
Matthieu

Antworten:

242

Eine eindeutige Einschränkung darf nicht mehr als 8000 Byte pro Zeile betragen und verwendet auch dann nur die ersten 900 Byte. Die sicherste maximale Größe für Ihre Schlüssel wäre also:

create table [misc_info]
( 
    [id] INTEGER PRIMARY KEY IDENTITY NOT NULL, 
    [key] nvarchar(450) UNIQUE NOT NULL, 
    [value] nvarchar(max) NOT NULL
)

Das heißt, der Schlüssel darf nicht länger als 450 Zeichen sein. Wenn Sie zu varcharanstatt wechseln können nvarchar(z. B. wenn Sie keine Zeichen von mehr als einer Codepage speichern müssen), kann dies zu 900 Zeichen führen.

Daniel Renshaw
quelle
1
Wäre das Limit für Varchar immer noch Varchar (450)?
Steam
9
Sie haben Platz, um entweder varchar(900)ODER zu verwenden nvarchar(450).
Daniel Renshaw
Mein Verständnis ist, dass ein Varchar 4 Bytes benötigt, um die Länge des Elements zu bestimmen, was bedeutet, dass das tatsächliche Limit Varchar sein muss (896). Ist das richtig?
Mrmillsy
2
@mrmillsy Die deklarierte maximale Größe enthält nicht den Overhead (2 Byte, nicht 4), und die Overhead-Bytes sind nicht in der Begrenzung der maximalen Indexzeilengröße enthalten. technet.microsoft.com/en-us/library/ms176089(v=sql.100).aspx
Daniel Renshaw
1
@mrmillsy Sie erhalten diese Nachricht, weil Sie die ID1 intin den Index aufnehmen. Das interfordert 4 Bytes zusätzlich zu den 900 Bytes für die varchar.
Daniel Renshaw
33

In SQL Server (bis 2008 R2) gibt es eine Einschränkung, dass varchar (MAX) und nvarchar (MAX) (und verschiedene andere Typen wie Text, ntext) nicht in Indizes verwendet werden können. Sie haben zwei Möglichkeiten:
1. Legen Sie eine begrenzte Größe für das Schlüsselfeld fest, z. nvarchar (100)
2. Erstellen Sie eine Prüfbedingung , die den Wert mit allen Schlüsseln in der Tabelle vergleicht. Die Bedingung ist:

([dbo].[CheckKey]([key])=(1))

und [dbo]. [CheckKey] ist eine Skalarfunktion, definiert als:

CREATE FUNCTION [dbo].[CheckKey]
(
    @key nvarchar(max)
)
RETURNS bit
AS
BEGIN
    declare @res bit
    if exists(select * from key_value where [key] = @key)
        set @res = 0
    else
        set @res = 1

    return @res
END

Beachten Sie jedoch, dass ein nativer Index leistungsfähiger ist als eine Prüfbedingung. Wenn Sie also keine Länge angeben können, verwenden Sie die Prüfbedingung nicht.

Marwan
quelle
Clever - schöner als Auslöser, fühle ich.
Neil Moss
14

Die einzige Lösung besteht darin, weniger Daten in Ihrem eindeutigen Index zu verwenden. Ihr Schlüssel kann höchstens NVARCHAR (450) sein.

"SQL Server behält das 900-Byte-Limit für die maximale Gesamtgröße aller Indexschlüsselspalten bei."

Lesen Sie mehr bei MSDN

Don
quelle
Wäre das Limit für Varchar immer noch Varchar (450)?
Steam
7

Eine Lösung wäre, Ihren Schlüssel als zu deklarieren nvarchar(20).

Klaus Byskov Pedersen
quelle
2

Wenn Sie den Kommentar von klaisbyskov zu Ihrer Schlüssellänge, die Gigabyte groß sein muss, zur Kenntnis nehmen und davon ausgehen, dass Sie dies tatsächlich benötigen, sind Ihre einzigen Optionen meiner Meinung nach:

  1. Verwenden Sie einen Hash des Schlüsselwerts
    • Erstellen Sie eine Spalte auf nchar (40) (zum Beispiel für einen sha1-Hash).
    • Setzen Sie einen eindeutigen Schlüssel in die Hash-Spalte.
    • Generieren Sie den Hash beim Speichern oder Aktualisieren des Datensatzes
  2. Trigger zum Abfragen der Tabelle nach einer vorhandenen Übereinstimmung beim Einfügen oder Aktualisieren.

Das Hashing ist mit der Einschränkung verbunden, dass Sie eines Tages eine Kollision bekommen könnten .

Trigger scannen die gesamte Tabelle.

Zu dir hinüber...

Neil Moss
quelle