【问题标题】:Flattern child/parent data with unknown number of columns使用未知列数扁平化子/父数据
【发布时间】:2016-04-11 15:58:44
【问题描述】:

我正在努力寻找以 SQL (MySQL DB) 和 C# windows 形式存储和表示数据的最佳方式。

我的数据映射到如下所示的类时;

public class Parent
{
        public string UniqueID { get; set; }   //Key
        public DateTime LoadTime { get; set; }
        public string Reference { get; set; }
        private List<Child> Elements { get; set; }
}

public class Child
{
        public int MemberCode { get; set; }   //Composite key
        public int ElementCode { get; set; }  //Composite key
        public Object Data { get; set; }
}

我的数据非常动态。所以父记录可以有任意数量的子记录。

在子记录中,MemberCode 和 ElementCode 实际上是其他表/类的外键,当执行查找时,它会为我提供有关数据实际内容的详细信息。例如

MemberCode = 1 & ElementCode = 1 means data is a Date
MemberCode = 1 & ElementCode = 3 means data is a telephone number
MemberCode = 2 & ElementCode = 11 means data is a Product Code
MemberCode = 2 & ElementCode = 12 means data is a Service Code
etc

这些有效地结合起来表明列名是什么,并且它们总是相同的(因此 MemberCode = 1 & ElementCode = 1 将始终是 Date 无论它与哪个子对象关联)。 目前这些是引用/查找,但我也可以将数据放在类中的变量中,因为这可能会更容易。那么它更像是一个键值对。

目前在我的数据库中,我将它们存储为两个表,子记录还包含来自父级的 UniqueID。但我不确定这是否是我将要解释的最佳方式。

我的表就是这样创建的

CREATE TABLE `PARENT` (
        `ID` INT(11) NOT NULL AUTO_INCREMENT,
        `LOADTIME` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
        `REFERENCE` VARCHAR(100) NOT NULL,
        PRIMARY KEY (`ID`)
    )

CREATE TABLE `CHILD` (
    `ID` INT(11) NOT NULL,
    `MEMBER_CODE` INT(11) NOT NULL,
    `ELEMENT_CODE` INT(11) NOT NULL,
    `DATA` VARCHAR(4000) NULL DEFAULT NULL,
    PRIMARY KEY (`ID`, `MEMBER_CODE`, `ELEMENT_CODE`),
    CONSTRAINT `fk_ID` FOREIGN KEY (`ID`) REFERENCES `Parent` (`ID`)
)

现在我想做的是展平这些数据,以便我可以将单个父记录与所有子记录显示为一行。理想情况下,我想在 ObjectListView (http://objectlistview.sourceforge.net/cs/index.html) 中显示它,但如果它能让生活更轻松,可以考虑使用 datagrid。

因为我的数据是动态的,所以我很难把它弄平,如果我选择 10 个父记录,那么每个记录可以有不同数量的子元素,每个可以有不同的 MemberCodes 和 ElementCode,这意味着它们是有效的不同的列。

所以我的数据可能如下所示(但规模更大);

但由于数据的动态特性,我很难做到这一点。在我的代码中的 SQL 或对象中。也许还有另一种更适合我的数据存储方式。

【问题讨论】:

  • 您能否为您的数据库对象编写脚本,以便我们了解您当前如何存储这些对象?另外,父母可以有几个具有相同元素代码的孩子吗?所有元素代码的列表是固定的还是动态的?
  • 我为表添加了数据库创建脚本,并澄清了memberCode,elementCode。基本上这些在所有子对象中总是相同的。
  • 还请说明属性列表是否固定 - 即您是否经常添加对 {MemberCode;ElementCode} - 或者它们是预定义的?
  • MemberCode (MC) 和 ElementCode (EC) 是预定义和固定的。 1 个 MC 可以有多个 EC。例如:MC 1 = 呼叫信息。这可以有多个 EC。 EC 1 = 日期,EC 3 = 电话号码。然后我可以有 MC 40,这可以有 EC 1 = 日期,EC 3 = 电话号码,EC 5 = 金额等。但是数据库中定义的 MC 和 EC,但我将它们读入参考表,因为它们是固定的(虽然理论上它们可以在每次部署时更改,任何更改往往是扩展而不是更改并且很少见。)

标签: c# mysql sql


【解决方案1】:

在为此工作了很多天之后,我自己设法解决了这个问题。我所做的是以下内容;

在我原来的子类中,MemberCode 和 ElementCode 创建了一个唯一键,基本上说明了列名是什么。所以我更进一步,添加了一个“Column_Name”,这样我就有了

public class Child
{
        public int MemberCode { get; set; }   //Composite key
        public int ElementCode { get; set; }  //Composite key
        public string Column_Name { get; set; }  //Unique.  Alternative Key
        public Object Data { get; set; }
}

这显然也反映在我的数据库表中。

我用来提取数据的 SQL 如下所示;

select  p.UniqueID, p.LoadTime, p.reference, c.MemberCode, c.ElementCode , c.column_name, c.Data 
from parent as p, child as c
where p.UniqueID = c.UniqueID 
//aditional filter criteria
ORDER BY p.UniqueID, MemberCode, ElementCode

首先按 UniqueID 排序对于确保记录以正确的顺序供以后处理至关重要。

我会使用dynamicExpandoObject() 来存储数据。

所以我对结果进行迭代以将sql结果转换为如下结构;

List<dynamic> allRecords = new List<dynamic>();  //A list of all my records
List<dynamic> singleRecord = null;  //A list representing just a single record

bool first = true;   //Needed for execution of the first iteration only
int lastId = 0;      //id of the last unique record

foreach (DataRow row in args.GetDataSet.Tables[0].Rows)
{
    int newID = Convert.ToInt32(row["UniqueID"]);  //get the current record unique id   

    if (newID != lastId)  //If new record then get header/parent information
    {
        if (!first)
            allRecords.Add(singleRecord);   //store the last record
        else
            first = false;

        //new object
        singleRecord = new List<dynamic>();

        //get parent information and store it
        dynamic head = new ExpandoObject();
        head.Column_name = "UniqueID";
        head.UDS_Data = row["UniqueID"].ToString();
        singleRecord.Add(head);

        head = new ExpandoObject();
        head.Column_name = "LoadTime";
        head.UDS_Data = row["LoadTime"].ToString();
        singleRecord.Add(head);

        head = new ExpandoObject();
        head.Column_name = "reference";
        head.UDS_Data = row["reference"].ToString();
        singleRecord.Add(head);                    
    }

    //get child information and store it.  One row at a time
    dynamic record = new ExpandoObject();

    record.Column_name = row["column_name"].ToString();
    record.UDS_Data = row["data"].ToString();
    singleRecord.Add(record);

    lastId = newID;   //store the last id
}
allRecords.Add(singleRecord);  //stores the last complete record

然后我以我需要的扁平方式动态存储我的信息。

现在下一个问题是我想使用的 ObjectListView。这不能接受这样的动态类型。

所以我在我的代码中存储了我想要的信息,但我仍然无法按要求显示它。

解决方案是使用ObjectListView 的变体,称为DataListView。这实际上是相同的控件,但可以是数据绑定的。 另一种选择也是使用 DatagridView,但出于其他原因,我想坚持使用 ObjectListView。

所以现在我必须将我的动态数据转换为数据源。我这样做如下;

DataTable dt = new DataTable();            
foreach (dynamic record in allRecords)
{
    DataRow dr = dt.NewRow();
    foreach (dynamic item in record)
    {
        var prop = (IDictionary<String, Object>)item;
        if (!dt.Columns.Contains(prop["Column_name"].ToString()))
        {
            dt.Columns.Add(new DataColumn(prop["Column_name"].ToString()));
        }

        dr[prop["Column_name"].ToString()] = prop["UDS_Data"];
    }
    dt.Rows.Add(dr);
}

然后我只需将我的数据源分配给 DataListView,生成列,嘿,现在我已经提取、展平并显示了我需要的动态数据。

【讨论】:

    猜你喜欢
    • 2021-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多