Ist es möglich, die maximale Festplattennutzung für einen Benutzer zu begrenzen?

7

Ist es möglich, eine Grenze für den physischen Speicherplatz festzulegen, den ein Benutzer (oder eine Gruppe von Benutzern) in einer einzelnen Datenbank verwenden kann? Gibt es beispielsweise eine Methode, um ein Schema einer Dateigruppe zuzuordnen und dann eine maximale Größe für die Dateigruppe festzulegen? Die eigentliche Datenbank würde andere Tabellen enthalten und daher andere Dateigruppen haben wollen, die unbegrenzt sind.

SQLMIKE
quelle
Werden diese Benutzer nach Belieben ihre eigenen Tabellen erstellen?
Aaron Bertrand
Ja, die Benutzer erstellen nach Belieben ihre eigenen Tabellen.

Antworten:

10

Wenn Ihre Benutzer über Erstellungsberechtigungen in ihrem eigenen Schema verfügen, können Sie einen DDL-Trigger einrichten, um die Tabellenerstellung zu erfassen, die in einer anderen Dateigruppe als der mit diesem Benutzer verknüpften erfolgt. Sie können nicht verhindern, dass die Tabelle erstellt wird, aber Sie können sie zurücksetzen und eine Fehlermeldung zurückgeben.

Fügen wir zunächst einer vorhandenen Datenbank eine Dateigruppe und eine Datei mit maximaler Größe hinzu:

USE [master];
GO

ALTER DATABASE floob ADD FILEGROUP SomeUser;
GO

ALTER DATABASE floob ADD FILE 
(
  NAME = SomeUser, 
  FILENAME = 'C:\...wherever...\floob_SomeUser.mdf',
  MAXSIZE = 20MB -- your quota, this part is important!
) TO FILEGROUP SomeUser; -- this part is also important!
GO

Erstellen wir nun ein Schema und eine Anmeldung, die dieses Schema steuern können:

CREATE SCHEMA SomeUser;
GO

CREATE LOGIN SomeUser WITH PASSWORD = 'foo', CHECK_POLICY = OFF;
GO

USE floob;
GO

CREATE USER SomeUser FROM LOGIN [SomeUser] WITH DEFAULT_SCHEMA = SomeUser;
GO

GRANT CONTROL ON SCHEMA::SomeUser TO dbo;
GRANT CREATE TABLE TO SomeUser;
GO

Erstens kann dieser Benutzer mit diesen Einstellungen keine Tabellen in dbo oder einem anderen Schema als dem eigenen erstellen. Dies bedeutet, dass er standardmäßig nur Tabellen in seiner begrenzten Datei [Gruppe] erstellen kann. Wenn Sie als dieser Benutzer eine Verbindung zum Datenbank-Floob herstellen und versuchen, eine Tabelle in dbo zu erstellen:

CREATE TABLE dbo.foo(id INT);

Sie erhalten diesen Fehler:

Nachricht 2760, Ebene 16,
Status 1, Zeile 1 Der angegebene Schemaname "dbo" ist entweder nicht vorhanden oder Sie haben keine Berechtigung, ihn zu verwenden.

Und aufgrund des Standardschemas dieses Benutzers wird bei Nichtangabe des SomeUser-Schemas immer noch eine in SomeUser erstellte Tabelle angezeigt (obwohl Sie hier, insbesondere in diesem Fall, unbedingt auf bewährte Methoden drängen und beim Erstellen oder Referenzieren von Objekten immer den Schemanamen angeben sollten ).

Jetzt können wir einen Trigger wie diesen erstellen, der den gesamten Stapel zurücksetzt, falls keine Dateigruppe angegeben ist:

CREATE TRIGGER EnsureFilegroup
ON DATABASE
FOR CREATE_TABLE
AS
BEGIN
  DECLARE @e XML = EVENTDATA();

  IF @e.value('(/EVENT_INSTANCE/SchemaName)[1]', 'NVARCHAR(128)') = N'SomeUser'
     AND NOT EXISTS (SELECT 1 FROM sys.indexes AS i INNER JOIN sys.data_spaces AS d
       ON i.data_space_id = d.data_space_id
       WHERE d.name = N'SomeUser'
       AND i.[object_id] = OBJECT_ID(
        QUOTENAME(@e.value('(/EVENT_INSTANCE/SchemaName)[1]',  'NVARCHAR(128)'))
        + '.' + QUOTENAME(@e.value('(/EVENT_INSTANCE/ObjectName)[1]',  'NVARCHAR(128)')))
       AND i.index_id IN (0,1))

  BEGIN
    RAISERROR('You must place your objects on the SomeUser filegroup.', 11, 1);
    ROLLBACK TRANSACTION;
  END
END
GO

Es ist besonders praktisch, die Namen von Benutzer, Schema und Dateigruppe gleich zu halten, was es einfacher macht, dies als programmatisch zu verwenden. Sie können zunächst überprüfen, ob das verwendete Schema nicht zu Ihren "umlimited" -Schemas gehört, und dann eine Prüfung durchführen, bei der der Schemaname mit dem Namen der Dateigruppe verglichen wird. Wenn es nicht übereinstimmt, hat dieser Benutzer Rechte für das falsche Schema und versucht, Objekte am falschen Ort zu erstellen. Es wird ein Fehler ausgegeben:

ALTER TRIGGER EnsureFilegroup
ON DATABASE
FOR CREATE_TABLE
AS
BEGIN
  DECLARE @e XML = EVENTDATA();

  IF @e.value('(/EVENT_INSTANCE/SchemaName)[1]', 'NVARCHAR(128)') <> N'dbo'
    -- or NOT IN (N'dbo', N'other unlimited schemas')
  BEGIN

    IF NOT EXISTS (SELECT 1 FROM sys.indexes AS i INNER JOIN sys.data_spaces AS d
       ON i.data_space_id = d.data_space_id
       WHERE d.name = @e.value('(/EVENT_INSTANCE/SchemaName)[1]', 'NVARCHAR(128)')
       AND i.[object_id] = OBJECT_ID(
        QUOTENAME(@e.value('(/EVENT_INSTANCE/SchemaName)[1]',  'NVARCHAR(128)'))
        + '.' + QUOTENAME(@e.value('(/EVENT_INSTANCE/ObjectName)[1]',  'NVARCHAR(128)')))
       AND i.index_id IN (0,1))
    BEGIN
      RAISERROR('You must place your objects on your own filegroup.', 11, 1);
      ROLLBACK TRANSACTION;
    END
  END
END
GO
Aaron Bertrand
quelle
2
Gutes Zeug Aaron. Sehr schön.
RBarryYoung
1
Lieben Sie einfach den Auslöser der Ensurefilegroup
Edward Dortland