用户定义类型中的列表(SQL Server 2008)

时间:2010-03-07 15:57:21

标签: sql-server-2008 clr user-defined-types

我正在尝试定义一种新类型,并且没有太多运气找到有关在其中使用列表的任何信息。基本上我的新类型将包含两个列表,比如SqlSingle类型的x和y(用户定义的类型是用C#编写的)这是否可能?

如果不是,你应该如何在SQL Server 2008专栏中模拟一个任意长度的两个列表呢?

我可能会以错误的方式解决这个问题,但这是我现在能想到的最佳方法。非常感谢任何帮助。

2 个答案:

答案 0 :(得分:2)

你可以在CLR UDT中使用List<T> - 虽然CLR类型是结构,它应该是不可变的,所以如果你没有一个非常令人信服的理由,ReadOnlyCollection<T>将是更好的选择为了可变性。在任何一种情况下你需要知道的是SQL不知道如何使用列表本身;你不能简单地将列表类型公开为公共IList<T>IEnumerable<T>,并且可以像你在纯.NET中那样快乐地使用。

通常,解决此问题的方法是公开Count属性和一些方法来获取单个列表项。

此外,在这种情况下,我不是维护两个单独的SqlSingle实例列表,而是创建一个表示单个点的附加类型,因此您可以单独管理它并在SQL中传递它,如果您需要到:

[Serializable]
[SqlUserDefinedType(Format.Native)]
public struct MyPoint
{
    private SqlSingle x;
    private SqlSingle y;

    public MyPoint()
    {
    }

    public MyPoint(SqlSingle x, SqlSingle y) : this()
    {
        this.x = x;
        this.y = y;
    }

    // You need this method because SQL can't use the ctors
    [SqlFunction(Name = "CreateMyPoint")]
    public static MyPoint Create(SqlSingle x, SqlSingle y)
    {
        return new MyPoint(x, y);
    }

    // Snip Parse method, Null property, etc.
}

主要类型看起来像这样:

[Serializable]
[SqlUserDefinedType(Format.UserDefined, IsByteOrdered = true, MaxByteSize = ...)]
public struct MyUdt
{
    // Make sure to initialize this in any constructors/builders
    private IList<MyPoint> points;

    [SqlMethod(OnNullCall = false, IsDeterministic = true, IsPrecise = true)]
    public MyPoint GetPoint(int index)
    {
        if ((index >= 0) && (index < points.Count))
        {
            return points[index];
        }
        return MyPoint.Null;
    }

    public int Count
    {
        get { return points.Count; }
    }
}

如果您需要SQL来获取所有点的序列,那么您也可以为序列类型添加一个可枚举的方法:

[SqlFunction(FillRowMethodName = "FillPointRow",
    TableDefinition = "[X] real, [Y] real")]
public static IEnumerable GetPoints(MyUdt obj)
{
    return obj.Points;
}

public static void FillPointRow(object obj, out SqlSingle x, out SqlSingle y)
{
    MyPoint point = (MyPoint)obj;
    x = point.X;
    y = point.Y;
}

您可能认为可以使用IEnumerable<T>和/或使用实例方法而不是静态方法,但是甚至不打算尝试,它不起作用。

因此,在SQL Server中使用结果类型的方式是:

DECLARE @UDT MyUdt
SET @UDT = <whatever>

-- Will show the number of points
SELECT @UDT.Count

-- Will show the binary representation of the second point
SELECT @UDT.GetPoint(1) AS [Point]

-- Will show the X and Y values for the second point
SELECT @UDT.GetPoint(1).X AS [X], @UDT.GetPoint(1).Y AS [Y]

-- Will show all the points
SELECT * FROM dbo.GetPoints(@UDT)

希望这有助于您走上正轨。在处理列表/序列数据时,UDT可能会变得非常复杂。

另请注意,您显然需要添加序列化方法,构建器方法,聚合方法等。这可能是一场相当严峻的考验;确保这实际上是您想要进入的方向,因为一旦您开始添加UDT列,如果您意识到您做出了错误的选择,则很难进行更改。

答案 1 :(得分:1)

您描述的列表通常是规范化的 - 也就是说,存储在单独的表中,每个项目有一行 - 而不是试图将它们塞进一个列中。如果您可以分享有关您要完成的内容的更多信息,也许我们可以提供更多帮助。

编辑 - 建议的表格结构:

-- route table--
route_id      int   (PK)
route_length  int (or whatever)
route_info    <other fields as needed>

-- waypoint table --
route_id      int      (PK)
sequence      tinyint  (PK)
lat           decimal(9,6)
lon           decimal(9,6)
waypoint_info <other fields as needed>