4

这与 SQL Server 2005(或更高版本)中的计算列和默认约束(可能还有其他表达式)有关。这两个都使用任意表达式来生成一个值,例如(year+1)对于表示“明年”的计算列(这显然是一个简单而愚蠢的例子)。

我正在尝试做的事情:我希望能够确定某个表中现有的计算列(或默认约束)是否与预期的定义匹配,后者是在用于创建的软件生成的模式中定义的桌子。我可以使用 获取计算列sp_helptext的定义,并从sys.default_constraints目录视图中获取默认约束的定义。

我遇到的问题:我从上述来源获得的表达式是规范化/标准形式,与用于创建列/约束的形式不匹配。在上面的示例中,SQL 将表达式规范化为([year]+(1)). 因此,这种形式和原始形式之间的简单字符串比较将无法可靠地确定它们是否相同。

我已经想到的解决方案:

  • 生成原始表达式,以便它们匹配 SQL 的形式。这需要了解 SQL 用来生成其表单的规则,这些规则没有记录在案,因此这不是一个很好的解决方案。
  • 将两种形式解析为 AST 并进行比较。我已经是原始形式的 AST,但我没有解析器,我宁愿不写。
  • 创建一个临时表并使用原始表达式添加一个计算列,然后读回规范化的表达式。这将非常可靠,但感觉很脏,因为理论上这种比较应该是只读操作。

谁能想到另一个处理这个问题的好选择?我的希望是,也许有人知道一些调试/诊断工具,它们会以标准化/标准形式返回输入表达式。

4

3 回答 3

3

我还要补充一点,表达式的“规范化”有时不仅会添加括号,还会以非常不同的方式改变一些表达式!

例如,在 MS SQL 2008 Express 中,我使用以下表达式创建了一个默认值:

year(getdate()) + 1

但 SQL Server 将其更改为

(datepart(year,getdate())+(1))

所以,我不相信任何一种规则或正则表达式都能100%解决你的问题,所以我建议你结合几种方法

1)首先,我认为在您的情况下,典型的约束数量有限,通常存在于大多数数据库中。通常,有 getdate() 和常量数值表达式 (0)、(1)。您可以拥有一个包含这些典型约束的表格,这些约束将帮助您匹配预期表达式和实际表达式。

2)然后您可以尝试非常简单的规则,将所有字段名称包含在 [] 括号中,并将所有常量和数学运算包含在 () 中,因此您将year + 1转换为([year] + (1))。我想这可以用正则表达式来完成。

3) 对于无法使用第一种或第二种方法比较预期结果和实际结果的所有情况,您将按照您的建议进行 - 创建一个临时表并比较结果。


编辑 04.Aug:

我发现当您创建数据库级默认值时,它们不会被规范化。奇怪,嗯?但是可能,您可以使用这个事实并创建绑定到列的数据库级默认值,而不是为列创建默认约束(不过,我认为这将是设计上的一个非常大的变化,并且需要对现有数据库进行大量更新)

至于列默认约束,以及动态创建/删除默认值以获得规范化形式的方法,这里是一个使用 Microsoft.SqlServer.Management.Smo 库的简单 C# 代码。我建议创建一个包含 IntTest int、VarcharTest varchar(1)、DateTimeTest datetime 等列的 test_table - 即每种类型只有一列。在这种情况下,您将创建/删除默认值,但不必创建删除表和列,这将提高性能。

将遵循 C# 代码(包括使用 Microsoft.SqlServer.Management.Smo;)

        Server server = new Server("localhost\\SQLEXPRESS");
        Database db = server.Databases["test"];
        Table t = db.Tables["test_defaults"];
        //here should be some logic to select column name depending on default data type
        //for example for DateTime defaults we will use "DateTimeTest" column
        Column c = t.Columns["DateTimeTest"];

        //clean up previous results if they exist
        DefaultConstraint constr = c.DefaultConstraint;
        if (constr != null) constr.Drop();

        //create new constraint
        constr = c.AddDefaultConstraint();
        constr.Text = "getdate()";
        constr.Create();
        //after refresh we will have a new text
        constr.Refresh();
        string result = constr.Text;

        //drop it if we don't need it
        constr.Drop();
于 2009-08-03T09:03:14.150 回答
0

我觉得应该通过连接到 DAC 来回答(以便您可以查询系统表),但我实际上无法找出函数 'object_definition' 的工作原理。

对于像 Kalen Delaney 这样的人来说,这可能是一个问题,要找出是否有一个公共函数可以用来解析这些东西。

于 2009-08-03T09:19:40.147 回答
0

我倾向于您的第三个解决方案(创建一个具有该约束的表并将其读回)-您可以使用临时表来执行此操作,因此它将是半干净的,并且如果您缓存规范化的表单,您只会当您正在搜索更改时需要这样做。我不确定您要搜索的表达式有多静态,但是当它们发生变化时,保存过程的一部分就是它创建一个临时表,应用约束,从定义中读取它,并将其保存具有约束的本机形式。

如果这不是您想要的(除了它不完全干净),请告诉我,我可以根据需要进行调整。

于 2009-08-03T17:40:01.920 回答