ขั้นตอนการจัดเก็บกลางเพื่อดำเนินการในบริบทฐานข้อมูลการโทร


17

ฉันกำลังทำงานกับโซลูชันการบำรุงรักษาที่กำหนดเองโดยใช้sys.dm_db_index_physical_statsมุมมอง ฉันมีมันถูกอ้างอิงจากขั้นตอนการจัดเก็บ ตอนนี้เมื่อโพรซีเดอร์ที่เก็บรันบนหนึ่งในฐานข้อมูลของฉันมันทำสิ่งที่ฉันต้องการให้ทำและดึงรายการของระเบียนทั้งหมดที่เกี่ยวข้องกับฐานข้อมูลใด ๆ ลง เมื่อฉันวางไว้ในฐานข้อมูลอื่นมันจะดึงรายการทั้งหมดที่เกี่ยวข้องกับฐานข้อมูลนั้นลง

ตัวอย่างเช่น (รหัสที่ด้านล่าง):

  • การค้นหาทำงานกับฐานข้อมูล 6 แสดงข้อมูล [ร้องขอ] สำหรับฐานข้อมูล 1-10
  • เคียวรีรันกับฐานข้อมูล 3 แสดงข้อมูล [ร้องขอ] สำหรับฐานข้อมูล 3 เท่านั้น

เหตุผลที่ฉันต้องการขั้นตอนนี้โดยเฉพาะในฐานข้อมูลสามเป็นเพราะฉันต้องการเก็บวัตถุการบำรุงรักษาทั้งหมดในฐานข้อมูลเดียวกัน ฉันต้องการให้งานนี้อยู่ในฐานข้อมูลการบำรุงรักษาและทำงานราวกับว่าอยู่ในฐานข้อมูลแอปพลิเคชันนั้น

รหัส:

ALTER PROCEDURE [dbo].[GetFragStats] 
    @databaseName   NVARCHAR(64) = NULL
    ,@tableName     NVARCHAR(64) = NULL
    ,@indexID       INT          = NULL
    ,@partNumber    INT          = NULL
    ,@Mode          NVARCHAR(64) = 'DETAILED'
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @databaseID INT, @tableID INT

    IF @databaseName IS NOT NULL
        AND @databaseName NOT IN ('tempdb','ReportServerTempDB')
    BEGIN
        SET @databaseID = DB_ID(@databaseName)
    END

    IF @tableName IS NOT NULL
    BEGIN
        SET @tableID = OBJECT_ID(@tableName)
    END

    SELECT D.name AS DatabaseName,
      T.name AS TableName,
      I.name AS IndexName,
      S.index_id AS IndexID,
      S.avg_fragmentation_in_percent AS PercentFragment,
      S.fragment_count AS TotalFrags,
      S.avg_fragment_size_in_pages AS PagesPerFrag,
      S.page_count AS NumPages,
      S.index_type_desc AS IndexType
    FROM sys.dm_db_index_physical_stats(@databaseID, @tableID, 
           @indexID, @partNumber, @Mode) AS S
    JOIN 
       sys.databases AS D ON S.database_id = D.database_id
    JOIN 
       sys.tables AS T ON S.object_id = T.object_id
    JOIN 
       sys.indexes AS I ON S.object_id = I.object_id
                        AND S.index_id = I.index_id
    WHERE 
        S.avg_fragmentation_in_percent > 10
    ORDER BY 
        DatabaseName, TableName, IndexName, PercentFragment DESC    
END
GO

4
@JoachimIsaksson ดูเหมือนว่าคำถามคือวิธีการมีหนึ่งสำเนาของขั้นตอนในฐานข้อมูลการบำรุงรักษาของพวกเขาที่อ้างอิง DMV ในฐานข้อมูลอื่น ๆ แทนที่จะต้องใส่สำเนาของขั้นตอนในแต่ละฐานข้อมูล
Aaron Bertrand

ขออภัยฉันไม่ชัดเจนมากขึ้นได้รับการจ้องมองที่นี่สองสามวัน แอรอนเป็นจุด ฉันต้องการให้ SP นี้อยู่ในฐานข้อมูลการบำรุงรักษาของฉันด้วยความสามารถในการดึงข้อมูลจากทั่วทั้งเซิร์ฟเวอร์ เมื่อมันอยู่ในฐานข้อมูลการบำรุงรักษาของฉันมันจะดึงข้อมูลการกระจายตัวเท่านั้นเกี่ยวกับฐานข้อมูลการบำรุงรักษาเอง สิ่งที่ฉันสับสนคือทำไมเมื่อฉันวาง SP เดียวกันนี้ในฐานข้อมูลที่แตกต่างกันและดำเนินการเหมือนกันมันดึงข้อมูลการกระจายตัวจากทั่วเซิร์ฟเวอร์หรือไม่ มีการตั้งค่าหรือสิทธิพิเศษที่จำเป็นต้องเปลี่ยนเพื่อให้ SP นี้ทำงานเป็นเช่นนี้จากฐานข้อมูลการบำรุงรักษาหรือไม่?

(โปรดทราบว่าวิธีการปัจจุบันของคุณเพิกเฉยต่อข้อเท็จจริงที่ว่าอาจมีสองตารางที่มีชื่อเดียวกันภายใต้สกีมาที่แตกต่างกันสอง - นอกเหนือจากคำแนะนำในคำตอบของฉันคุณอาจต้องการพิจารณาชื่อสกีมาเป็นส่วนหนึ่งของอินพุตและ / หรือเอาท์พุท)
Aaron Bertrand

คำตอบ:


15

วิธีหนึ่งคือการสร้างโพรซีเดอร์ระบบmasterแล้วสร้าง wrapper ในฐานข้อมูลการบำรุงรักษาของคุณ โปรดทราบว่าจะใช้งานได้กับฐานข้อมูลเดียวเท่านั้นในแต่ละครั้ง

ครั้งแรกในต้นแบบ:

USE [master];
GO
CREATE PROCEDURE dbo.sp_GetFragStats -- sp_prefix required
  @tableName    NVARCHAR(128) = NULL,
  @indexID      INT           = NULL,
  @partNumber   INT           = NULL,
  @Mode         NVARCHAR(20)  = N'DETAILED'
AS
BEGIN
  SET NOCOUNT ON;

  SELECT
    DatabaseName    = DB_NAME(),
    TableName       = t.name,
    IndexName       = i.name,
    IndexID         = s.index_id,
    PercentFragment = s.avg_fragmentation_in_percent,
    TotalFrags      = s.fragment_count,
    PagesPerFrag    = s.avg_fragment_size_in_pages,
    NumPages        = s.page_count,
    IndexType       = s.index_type_desc
    -- shouldn't s.partition_number be part of the output as well?
  FROM sys.tables AS t
  INNER JOIN sys.indexes AS i
    ON t.[object_id] = i.[object_id]
    AND i.index_id = COALESCE(@indexID, i.index_id)
    AND t.name = COALESCE(@tableName, t.name)
  CROSS APPLY
    sys.dm_db_index_physical_stats(DB_ID(), t.[object_id], 
      i.index_id, @partNumber, @Mode) AS s
  WHERE s.avg_fragmentation_in_percent > 10
  -- probably also want to filter on minimum page count too
  -- do you really care about a table that has 100 pages?
  ORDER BY 
    DatabaseName, TableName, IndexName, PercentFragment DESC;
END
GO
-- needs to be marked as a system object:
EXEC sp_MS_MarkSystemObject N'dbo.sp_GetFragStats';
GO

ในฐานข้อมูลการบำรุงรักษาของคุณให้สร้าง wrapper ที่ใช้ dynamic SQL เพื่อตั้งค่าบริบทให้ถูกต้อง:

USE YourMaintenanceDatabase;
GO
CREATE PROCEDURE dbo.GetFragStats
  @DatabaseName SYSNAME,      -- can't really be NULL, right?
  @tableName    NVARCHAR(128) = NULL,
  @indexID      INT           = NULL,
  @partNumber   INT           = NULL,
  @Mode         NVARCHAR(20)  = N'DETAILED'
AS
BEGIN
  DECLARE @sql NVARCHAR(MAX);

  SET @sql = N'USE ' + QUOTENAME(@DatabaseName) + ';
    EXEC dbo.sp_GetFragStats @tableName, @indexID, @partNumber, @Mode;';

  EXEC sp_executesql 
    @sql,
    N'@tableName NVARCHAR(128),@indexID INT,@partNumber INT,@Mode NVARCHAR(20)',
    @tableName, @indexID, @partNumber, @Mode;
END
GO

(เหตุผลที่ชื่อฐานข้อมูลไม่สามารถเป็นจริงได้NULLเพราะคุณไม่สามารถเข้าร่วมกับสิ่งที่ชอบsys.objectsและsys.indexesเนื่องจากมีอยู่อย่างอิสระในแต่ละฐานข้อมูลดังนั้นอาจมีขั้นตอนที่แตกต่างกันหากคุณต้องการข้อมูลทั่วทั้งอินสแตนซ์)

ตอนนี้คุณสามารถเรียกสิ่งนี้สำหรับฐานข้อมูลอื่น ๆ เช่น

EXEC YourMaintenanceDatabase.dbo.GetFragStats 
  @DatabaseName = N'AdventureWorks2012',
  @TableName    = N'SalesOrderHeader';

และคุณสามารถสร้าง a synonymในแต่ละฐานข้อมูลได้ตลอดเวลาดังนั้นคุณไม่จำเป็นต้องอ้างอิงชื่อของฐานข้อมูลการบำรุงรักษา:

USE SomeOtherDatabase;`enter code here`
GO
CREATE SYNONYM dbo.GetFragStats FOR YourMaintenanceDatabase.dbo.GetFragStats;

อีกวิธีหนึ่งก็คือการใช้ไดนามิก SQL อย่างไรก็ตามสิ่งนี้ก็จะใช้ได้กับฐานข้อมูลเดียวเท่านั้นในแต่ละครั้ง:

USE YourMaintenanceDatabase;
GO
CREATE PROCEDURE dbo.GetFragStats
  @DatabaseName SYSNAME,
  @tableName    NVARCHAR(128) = NULL,
  @indexID      INT           = NULL,
  @partNumber   INT           = NULL,
  @Mode         NVARCHAR(20)  = N'DETAILED'
AS
BEGIN
  SET NOCOUNT ON;

  DECLARE @sql NVARCHAR(MAX) = N'SELECT
    DatabaseName    = @DatabaseName,
    TableName       = t.name,
    IndexName       = i.name,
    IndexID         = s.index_id,
    PercentFragment = s.avg_fragmentation_in_percent,
    TotalFrags      = s.fragment_count,
    PagesPerFrag    = s.avg_fragment_size_in_pages,
    NumPages        = s.page_count,
    IndexType       = s.index_type_desc
  FROM ' + QUOTENAME(@DatabaseName) + '.sys.tables AS t
  INNER JOIN ' + QUOTENAME(@DatabaseName) + '.sys.indexes AS i
    ON t.[object_id] = i.[object_id]
    AND i.index_id = COALESCE(@indexID, i.index_id)
    AND t.name = COALESCE(@tableName, t.name)
  CROSS APPLY
    ' + QUOTENAME(@DatabaseName) + '.sys.dm_db_index_physical_stats(
        DB_ID(@DatabaseName), t.[object_id], i.index_id, @partNumber, @Mode) AS s
  WHERE s.avg_fragmentation_in_percent > 10
  ORDER BY 
    DatabaseName, TableName, IndexName, PercentFragment DESC;';

  EXEC sp_executesql @sql, 
    N'@DatabaseName SYSNAME, @tableName NVARCHAR(128), @indexID INT,
      @partNumber INT, @Mode NVARCHAR(20)',
    @DatabaseName, @tableName, @indexID, @partNumber, @Mode;
END
GO

อีกวิธีหนึ่งก็คือการสร้างมุมมอง (หรือฟังก์ชันที่มีค่าเป็นตาราง) เพื่อรวมชื่อตารางและดัชนีของฐานข้อมูลทั้งหมดของคุณอย่างไรก็ตามคุณต้องเข้ารหัสรหัสชื่อฐานข้อมูลลงในมุมมองและบำรุงรักษาตามที่คุณเพิ่ม / ลบฐานข้อมูลที่คุณต้องการอนุญาตให้รวมอยู่ในแบบสอบถามนี้ สิ่งนี้จะช่วยให้คุณสามารถดึงข้อมูลสถิติสำหรับฐานข้อมูลหลาย ๆ ไฟล์พร้อมกันได้

ก่อนมุมมอง:

CREATE VIEW dbo.CertainTablesAndIndexes
AS
  SELECT 
    db = N'AdventureWorks2012',
    t.[object_id],
    [table] = t.name,
    i.index_id,
    [index] = i.name
  FROM AdventureWorks2012.sys.tables AS t
  INNER JOIN AdventureWorks2012.sys.indexes AS i
  ON t.[object_id] = i.[object_id]

  UNION ALL

  SELECT 
    db = N'database2',
    t.[object_id],
    [table] = t.name,
    i.index_id,
    [index] = i.name
  FROM database2.sys.tables AS t
  INNER JOIN database2.sys.indexes AS i
  ON t.[object_id] = i.[object_id]

  -- ... UNION ALL ...
  ;
GO

จากนั้นขั้นตอน:

CREATE PROCEDURE dbo.GetFragStats
  @DatabaseName NVARCHAR(128) = NULL,
  @tableName    NVARCHAR(128) = NULL,
  @indexID      INT           = NULL,
  @partNumber   INT           = NULL,
  @Mode         NVARCHAR(20)  = N'DETAILED'
AS
BEGIN
  SET NOCOUNT ON;

  SELECT
    DatabaseName    = DB_NAME(s.database_id),
    TableName       = v.[table],
    IndexName       = v.[index],
    IndexID         = s.index_id,
    PercentFragment = s.avg_fragmentation_in_percent,
    TotalFrags      = s.fragment_count,
    PagesPerFrag    = s.avg_fragment_size_in_pages,
    NumPages        = s.page_count,
    IndexType       = s.index_type_desc
  FROM dbo.CertainTablesAndIndexes AS v
  CROSS APPLY sys.dm_db_index_physical_stats
    (DB_ID(v.db), v.[object_id], v.index_id, @partNumber, @Mode) AS s
  WHERE s.avg_fragmentation_in_percent > 10
    AND v.index_id = COALESCE(@indexID, v.index_id)
    AND v.[table] = COALESCE(@tableName, v.[table])
    AND v.db = COALESCE(@DatabaseName, v.db)
  ORDER BY 
    DatabaseName, TableName, IndexName, PercentFragment DESC;
END
GO

15

มีข่าวดีข่าวดีพร้อมที่จับและข่าวดีจริงๆ

ข่าวร้าย

วัตถุ T-SQL ทำงานในฐานข้อมูลที่พวกเขาอยู่ มีข้อยกเว้นสองข้อ (ไม่มีประโยชน์มาก):

  1. โพรซีเดอร์ที่เก็บไว้พร้อมชื่อนำหน้าด้วยsp_และที่มีอยู่ใน[master]ฐานข้อมูล (ไม่ใช่ตัวเลือกที่ยอดเยี่ยม: ฐานข้อมูลครั้งละหนึ่งฐานเพิ่มบางสิ่ง[master]อาจเพิ่มคำพ้องกับฐานข้อมูลแต่ละฐานข้อมูลซึ่งจะต้องดำเนินการสำหรับฐานข้อมูลใหม่แต่ละแห่ง)
  2. วิธีการจัดเก็บชั่วคราว - ท้องถิ่นและระดับโลก (ไม่ได้เป็นตัวเลือกปฏิบัติตามที่พวกเขาจะต้องมีการสร้างขึ้นในแต่ละเวลาและทำให้คุณมีปัญหาเดียวกับที่คุณมีกับsp_proc [master]เก็บไว้ใน

ข่าวดี (พร้อมที่จับ)

หลายคน (อาจจะมากที่สุด) ผู้คนตระหนักถึงฟังก์ชั่นในตัวเพื่อรับเมตาดาต้าทั่วไป:

การใช้ฟังก์ชั่นเหล่านี้สามารถกำจัดความจำเป็นในการเข้าร่วมsys.databases(แม้ว่านี่ไม่ใช่ปัญหาจริงๆ), sys.objects( sys.tablesซึ่งเป็นที่นิยมมากกว่าซึ่งไม่รวมมุมมองที่จัดทำดัชนี) และsys.schemas(คุณพลาดที่หนึ่งและไม่ใช่ทุกอย่างอยู่ในdboสคีมา ;-) แต่ถึงแม้จะมีการลบสามในสี่ของการเข้าร่วมเรายังคงทำหน้าที่เหมือนเดิมใช่มั้ย ผิด o!

หนึ่งในคุณสมบัติที่ดีของOBJECT_NAME()และฟังก์ชั่นคือการที่พวกเขามีพารามิเตอร์ที่สองเป็นตัวเลือกสำหรับOBJECT_SCHEMA_NAME() @database_idความหมายในขณะที่เข้าร่วมกับตารางเหล่านั้น (ยกเว้นสำหรับsys.databases) เป็นเฉพาะฐานข้อมูลการใช้ฟังก์ชั่นเหล่านี้จะช่วยให้คุณได้รับข้อมูลทั่วทั้งเซิร์ฟเวอร์ แม้แต่OBJECT_ID () ก็อนุญาตให้ใช้ข้อมูลทั่วทั้งเซิร์ฟเวอร์ด้วยการให้ชื่อวัตถุที่ผ่านการรับรองโดยสมบูรณ์

โดยการรวมฟังก์ชั่นเมตาดาต้าเหล่านี้เข้ากับเคียวรีหลักทำให้เราสามารถลดความซับซ้อนในขณะเดียวกันก็ขยายออกไปเกินกว่าฐานข้อมูลปัจจุบัน การส่งผ่านซ้ำครั้งแรกของแบบสอบถามทำให้เรา:

SELECT  DB_NAME(stat.database_id) AS [DatabaseName],
        OBJECT_SCHEMA_NAME(stat.[object_id], stat.database_id) AS [SchemaName],
        OBJECT_NAME(stat.[object_id], stat.database_id) AS [TableName],
        ind.name AS [IndexName],
        stat.index_id AS [IndexID],
        stat.avg_fragmentation_in_percent AS [PercentFragment],
        stat.fragment_count AS [TotalFrags],
        stat.avg_fragment_size_in_pages AS [PagesPerFrag],
        stat.page_count AS [NumPages],
        stat.index_type_desc AS [IndexType]
FROM sys.dm_db_index_physical_stats(@DatabaseID, @TableID, 
        @IndexID, @PartitionNumber, @Mode) stat
INNER JOIN sys.indexes ind
        ON ind.[object_id] = stat.[object_id]
       AND ind.[index_id] = stat.[index_id]
WHERE stat.avg_fragmentation_in_percent > 10
ORDER BY DatabaseName, TableName, IndexName, PercentFragment DESC;

และในตอนนี้สำหรับ "catch": ไม่มีฟังก์ชั่น meta-data ที่จะได้รับชื่อดัชนีให้ใช้เซิร์ฟเวอร์เดียว งั้นเหรอ? พวกเรา 90% เสร็จสมบูรณ์และยังคงติดขัดอยู่ในฐานข้อมูลเฉพาะเพื่อรับsys.indexesข้อมูลหรือไม่? เราจำเป็นต้องสร้างโพรซีเดอร์ที่เก็บไว้เพื่อใช้ Dynamic SQL เพื่อเติมข้อมูลในแต่ละครั้งที่ proc หลักของเรารันตาราง temp ของsys.indexesรายการทั้งหมดในฐานข้อมูลทั้งหมดเพื่อให้เราสามารถเข้าร่วมได้หรือไม่ NO!

ข่าวดีจริงๆ

ดังนั้นคุณลักษณะเล็ก ๆ น้อย ๆ ที่บางคนชอบที่จะเกลียด แต่เมื่อใช้อย่างเหมาะสมแล้วก็สามารถทำสิ่งที่น่าอัศจรรย์ได้ ใช่: SQLCLR ทำไม? เนื่องจากฟังก์ชั่น SQLCLR สามารถส่งคำสั่ง SQL ได้อย่างชัดเจน แต่โดยธรรมชาติแล้วการส่งจากรหัสแอปมันเป็น Dynamic SQL ดังนั้นไม่เหมือนกับฟังก์ชัน T-SQL ฟังก์ชัน SQLCLR สามารถแทรกชื่อฐานข้อมูลลงในแบบสอบถามก่อนที่จะเรียกใช้งาน ความหมายเราสามารถสร้างฟังก์ชั่นของเราเองเพื่อสะท้อนความสามารถของOBJECT_NAME()และOBJECT_SCHEMA_NAME()ที่จะใช้database_idและได้รับข้อมูลสำหรับฐานข้อมูลที่

รหัสต่อไปนี้เป็นฟังก์ชั่นนั้น แต่ใช้ชื่อฐานข้อมูลแทน ID ดังนั้นจึงไม่จำเป็นต้องทำขั้นตอนเพิ่มเติมในการค้นหา (ซึ่งทำให้ซับซ้อนน้อยลงและเร็วขึ้นเล็กน้อย)

public class MetaDataFunctions
{
    [return: SqlFacet(MaxSize = 128)]
    [Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true, IsPrecise = true,
        SystemDataAccess = SystemDataAccessKind.Read)]
    public static SqlString IndexName([SqlFacet(MaxSize = 128)] SqlString DatabaseName,
        SqlInt32 ObjectID, SqlInt32 IndexID)
    {
        string _IndexName = @"<unknown>";

        using (SqlConnection _Connection =
                                    new SqlConnection("Context Connection = true;"))
        {
            using (SqlCommand _Command = _Connection.CreateCommand())
            {
                _Command.CommandText = @"
SELECT @IndexName = si.[name]
FROM   [" + DatabaseName.Value + @"].[sys].[indexes] si
WHERE  si.[object_id] = @ObjectID
AND    si.[index_id] = @IndexID;
";

                SqlParameter _ParamObjectID = new SqlParameter("@ObjectID",
                                               SqlDbType.Int);
                _ParamObjectID.Value = ObjectID.Value;
                _Command.Parameters.Add(_ParamObjectID);

               SqlParameter _ParamIndexID = new SqlParameter("@IndexID", SqlDbType.Int);
                _ParamIndexID.Value = IndexID.Value;
                _Command.Parameters.Add(_ParamIndexID);

                SqlParameter _ParamIndexName = new SqlParameter("@IndexName",
                                                  SqlDbType.NVarChar, 128);
                _ParamIndexName.Direction = ParameterDirection.Output;
                _Command.Parameters.Add(_ParamIndexName);

                _Connection.Open();
                _Command.ExecuteNonQuery();

                if (_ParamIndexName.Value != DBNull.Value)
                {
                    _IndexName = (string)_ParamIndexName.Value;
                }
            }
        }

        return _IndexName;
    }
}

หากคุณจะสังเกตเห็นว่าเรากำลังใช้การเชื่อมต่อบริบทซึ่งไม่เพียง แต่รวดเร็ว แต่ยังใช้งานได้ในSAFEแอสเซมบลี ใช่งานนี้ทำในชุดที่ทำเครื่องหมายว่าSAFEดังนั้น (หรือรูปแบบของมัน) ควรทำงานกับ Azure SQL Database V12 (การสนับสนุนสำหรับ SQLCLR ถูกลบออกค่อนข้างฉับพลันจากฐานข้อมูล SQL Azure ในเดือนเมษายน 2016)

ดังนั้นการสร้างการผ่านใหม่ครั้งที่สองของการสืบค้นหลักทำให้เรามีดังต่อไปนี้:

SELECT  DB_NAME(stat.database_id) AS [DatabaseName],
        OBJECT_SCHEMA_NAME(stat.[object_id], stat.database_id) AS [SchemaName],
        OBJECT_NAME(stat.[object_id], stat.database_id) AS [TableName],
        dbo.IndexName(DB_NAME(stat.database_id), stat.[object_id], stat.[index_id])
                     AS [IndexName],
        stat.index_id AS [IndexID],
        stat.avg_fragmentation_in_percent AS [PercentFragment],
        stat.fragment_count AS [TotalFrags],
        stat.avg_fragment_size_in_pages AS [PagesPerFrag],
        stat.page_count AS [NumPages],
        stat.index_type_desc AS [IndexType]
FROM sys.dm_db_index_physical_stats(@DatabaseID, @TableID, 
        @IndexID, @PartitionNumber, @Mode) stat
WHERE stat.avg_fragmentation_in_percent > 10
ORDER BY DatabaseName, TableName, IndexName, PercentFragment DESC;

แค่นั้นแหละ! ทั้ง SQLCLR Scalar UDF และขั้นตอนการเก็บรักษา T-SQL ของคุณสามารถอยู่ใน[maintenance]ฐานข้อมูลส่วนกลางเดียวกันได้ และคุณไม่จำเป็นต้องดำเนินการฐานข้อมูลครั้งละหนึ่งฐานข้อมูล ตอนนี้คุณมีฟังก์ชั่นเมตาดาต้าสำหรับข้อมูลทั้งหมดที่ขึ้นกับเซิร์ฟเวอร์

PS ไม่มีการ.IsNullตรวจสอบพารามิเตอร์อินพุตในรหัส C # เนื่องจากอ็อบเจ็กต์ wrapper T-SQL ควรสร้างด้วยWITH RETURNS NULL ON NULL INPUTตัวเลือก:

CREATE FUNCTION [dbo].[IndexName]
                   (@DatabaseName [nvarchar](128), @ObjectID [int], @IndexID [int])
RETURNS [nvarchar](128) WITH EXECUTE AS CALLER, RETURNS NULL ON NULL INPUT
AS EXTERNAL NAME [{AssemblyName}].[MetaDataFunctions].[IndexName];

หมายเหตุเพิ่มเติม:

  • วิธีที่อธิบายไว้ที่นี่ยังสามารถใช้เพื่อแก้ไขปัญหาอื่น ๆ ที่คล้ายกันมากของฟังก์ชั่นเมตาดาต้าข้ามฐานข้อมูลที่ขาดหายไป คำแนะนำ Microsoft Connect ต่อไปนี้เป็นตัวอย่างของกรณีดังกล่าว และเมื่อเห็นว่าไมโครซอฟท์ได้ปิดมันในฐานะ "จะไม่แก้ไข" เป็นที่ชัดเจนว่าพวกเขาไม่สนใจที่จะจัดหาฟังก์ชั่นในตัวที่OBJECT_NAME()จะตอบสนองความต้องการนี้ (ดังนั้นการแก้ปัญหาที่โพสต์บนคำแนะนำนั้น :-)

    เพิ่มฟังก์ชั่นเมทาดาทาเพื่อรับชื่อวัตถุจาก hobt_id

  • หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับการใช้ SQLCLR โปรดดูซีรี่ส์Stairway ไปยัง SQLCLR ที่ฉันเขียนบน SQL Server Central (จำเป็นต้องลงทะเบียนฟรีขออภัยฉันไม่ได้ควบคุมนโยบายของไซต์นั้น)

  • IndexName()ฟังก์ชั่น SQLCLR ดังกล่าวไม่สามารถใช้งานได้ก่อนรวบรวมในที่ง่ายต่อการติดตั้งสคริปต์บน Pastebin สคริปต์ช่วยให้คุณสมบัติ "CLR บูรณาการ" SAFEถ้ามันไม่ได้เปิดใช้แล้วและสภาถูกทำเครื่องหมายเป็น มันถูกคอมไพล์กับ. NET Framework เวอร์ชัน 2.0 เพื่อให้สามารถทำงานได้ใน SQL Server 2005 และใหม่กว่า (เช่นทุกรุ่นที่รองรับ SQLCLR)

    ฟังก์ชัน SQLCLR Meta-data สำหรับดัชนีข้ามฐานข้อมูล ()

  • หากใครสนใจIndexName()ฟังก์ชั่น SQLCLR และฟังก์ชั่นอื่น ๆ อีกกว่า 320 ฟังก์ชั่นและขั้นตอนการจัดเก็บมันมีอยู่ในSQL # library (ซึ่งฉันเป็นผู้เขียน) โปรดทราบว่าในขณะที่มีรุ่นฟรีฟังก์ชันSys_IndexNameมีให้เฉพาะในรุ่นเต็ม (พร้อมกับฟังก์ชันSys_AssemblyName ที่คล้ายกัน)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.