14

冒着被降级的风险,我想问一下使用 C# 从具有很多列的表中查看数据的最佳机制(对于此处固有的实践违规而言,最好显然是主观的)。很多,我的意思是1000左右。

现在,在您获得所有点击满意或抛出诸如“您为什么会有这么多列的表格”之类的响应之前,让我说这实际上是设计要求的一部分。我们正在尽可能快地从 1000 个数据点收集数据。我们需要尽可能快地存储这些,因此是平面表。数据需要可以从 SQL Server 直接访问,因此需要数据库(我们使用 SQL Compact 和 table-direct)。

所以现在让我们忘记我们所学到的关于正确的数据库设计、规范化规则等的所有知识,只关注我有一个包含 1000 列的表并且我希望能够在屏幕以验证数据是否确实进入了那里。

我试过数据网格。它呕吐是因为(不足为奇)它不是为处理那么多列而设计的。

我试过在 Studio 中使用查看器。它在 256 之后呕吐,而且最终用户无论如何都不会安装 Studio。

目前,结果不需要漂亮,不需要可更新,也不需要对数据更改敏感 - 只是给定时间点表中数据的静态快照。

相关(或半相关)信息:

  • 表有 1000 列(在点击满意之前阅读上文)
  • 使用 SQL Compact 3.5 版
  • 在桌面上运行
  • 寻找托管代码的答案
4

21 回答 21

15

如果你要实现自己的自定义用户控件,你可以像这样做一个鱼眼网格:

死图链接

这个例子展示了一个全尺寸的 3x4 面板在 9x10 的桌子内移动。因为(我假设)您不需要编辑这些数据,所以 UI 可能只是用户抓住面板并拖动它的地方。如果你真的很受虐和/或有很多空闲时间,你甚至可以在同一个网格上有多个鱼眼面板,让你可以同时比较网格的一个或多个区域。

更新: Silverlight显然有其中之一。有点。

于 2008-11-06T05:24:42.563 回答
14

您可以将所有数字格式化为带有空格的 n 个字符串,然后以固定宽度的字体显示它们。

1       2       3       4       6      36     436    6346
2       3       4       6      36     436    6346       0
3       4       6      36     436    6346       3       4
4       6      36     436    6346     333     222     334
于 2008-11-06T02:21:19.627 回答
12

好的,对我来说正确的答案是使用ReportViewer 控件,但没有以任何方式记录在 MSDN 中。问题是我有动态数据,所以我需要一个动态报告,并且所有的教程等似乎都假设您在设计时可以了解所有内容,因此您可以通过向导指向并单击自己的方式。

该解决方案最终需要几件。首先,我必须创建代码来动态生成 ReportViewer 用来描述报表布局以及哪些数据字段映射到什么的 RDLC。这就是我想出的:

public static Stream BuildRDLCStream(
    DataSet data, string name, string reportXslPath)
{
  using (MemoryStream schemaStream = new MemoryStream())
  {
    // save the schema to a stream
    data.WriteXmlSchema(schemaStream);
    schemaStream.Seek(0, SeekOrigin.Begin);

    // load it into a Document and set the Name variable
    XmlDocument xmlDomSchema = new XmlDocument();
    xmlDomSchema.Load(schemaStream);        
    xmlDomSchema.DocumentElement.SetAttribute("Name", data.DataSetName);

    // load the report's XSL file (that's the magic)
    XslCompiledTransform xform = new XslCompiledTransform();
    xform.Load(reportXslPath);

    // do the transform
    MemoryStream rdlcStream = new MemoryStream();
    XmlWriter writer = XmlWriter.Create(rdlcStream);
    xform.Transform(xmlDomSchema, writer);
    writer.Close();
    rdlcStream.Seek(0, SeekOrigin.Begin);

    // send back the RDLC
    return rdlcStream;
  }
}

第二部分是我直接从Dan Shipe 的博客中获取的 XSL 文件。那里的 RDLC 代码毫无价值,因为它都是为 Web 使用而设计的,但 XSL 是纯金的。为了完整起见,我把它放在这篇文章的底部,以防博客离线。

一旦我有了这两个部分,只需创建一个带有 ReportViewer 控件的表单,然后使用这段代码进行设置:

ds.DataSetName = name;

Stream rdlc = RdlcEngine.BuildRDLCStream(
    ds, name, "c:\\temp\\rdlc\\report.xsl");

reportView.LocalReport.LoadReportDefinition(rdlc);
reportView.LocalReport.DataSources.Clear();
reportView.LocalReport.DataSources.Add(
    new ReportDataSource(ds.DataSetName, ds.Tables[0]));
reportView.RefreshReport();

这里的关键是“ds”是一个 DataSet 对象,其中有一个 DataTable,其中包含要显示的数据。

同样,为了完整起见,这里是 XSL - 对大小感到抱歉:

    <?xml version="1.0"?>
    <!-- Stylesheet for creating ReportViewer RDLC documents -->
    <xsl:stylesheet version="1.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:msxsl="urn:schemas-microsoft-com:xslt"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
      xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner"  xmlns="http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition"
      >

     <xsl:variable name="mvarName" select="/xs:schema/@Name"/>
     <xsl:variable name="mvarFontSize">8pt</xsl:variable>
     <xsl:variable name="mvarFontWeight">500</xsl:variable>
     <xsl:variable name="mvarFontWeightBold">700</xsl:variable>


     <xsl:template match="/">
      <xsl:apply-templates select="/xs:schema/xs:element/xs:complexType/xs:choice/xs:element/xs:complexType/xs:sequence">
      </xsl:apply-templates>
     </xsl:template>

     <xsl:template match="xs:sequence">
      <Report xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner" xmlns="http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition">
       <BottomMargin>1in</BottomMargin>
       <RightMargin>1in</RightMargin>
       <LeftMargin>1in</LeftMargin>
       <TopMargin>1in</TopMargin>
       <InteractiveHeight>11in</InteractiveHeight>
       <InteractiveWidth>8.5in</InteractiveWidth>
       <Width>6.5in</Width>
       <Language>en-US</Language>
       <rd:DrawGrid>true</rd:DrawGrid>
       <rd:SnapToGrid>true</rd:SnapToGrid>
       <rd:ReportID>7358b654-3ca3-44a0-8677-efe0a55c7c45</rd:ReportID>

       <xsl:call-template name="BuildDataSource">
       </xsl:call-template>

       <xsl:call-template name="BuildDataSet">
       </xsl:call-template>

       <Body>
        <Height>0.50in</Height>
        <ReportItems>
         <Table Name="table1">
          <DataSetName><xsl:value-of select="$mvarName" /></DataSetName>
          <Top>0.5in</Top>
          <Height>0.50in</Height>
          <Header>
           <TableRows>
            <TableRow>
             <Height>0.25in</Height>
             <TableCells>

              <xsl:apply-templates select="xs:element" mode="HeaderTableCell">
              </xsl:apply-templates>

             </TableCells>
            </TableRow>
           </TableRows>
          </Header>
          <Details>
           <TableRows>
            <TableRow>
             <Height>0.25in</Height>
             <TableCells>

              <xsl:apply-templates select="xs:element" mode="DetailTableCell">
              </xsl:apply-templates>

             </TableCells>
            </TableRow>
           </TableRows>
          </Details>
          <TableColumns>

           <xsl:apply-templates select="xs:element" mode="TableColumn">
           </xsl:apply-templates>

          </TableColumns>
         </Table>
        </ReportItems>
       </Body>
      </Report>
     </xsl:template>

     <xsl:template name="BuildDataSource">
      <DataSources>
       <DataSource Name="DummyDataSource">
        <ConnectionProperties>
         <ConnectString/>
         <DataProvider>SQL</DataProvider>
        </ConnectionProperties>
        <rd:DataSourceID>84635ff8-d177-4a25-9aa5-5a921652c79c</rd:DataSourceID>
       </DataSource>
      </DataSources>
     </xsl:template>

     <xsl:template name="BuildDataSet">
      <DataSets>
       <DataSet Name="{$mvarName}">
        <Query>
         <rd:UseGenericDesigner>true</rd:UseGenericDesigner>
         <CommandText/>
         <DataSourceName>DummyDataSource</DataSourceName>
        </Query>
        <Fields>

         <xsl:apply-templates select="xs:element" mode="Field">
         </xsl:apply-templates>

        </Fields>
       </DataSet>
      </DataSets>
     </xsl:template>

     <xsl:template match="xs:element" mode="Field">
      <xsl:variable name="varFieldName"> 
       <xsl:value-of select="@name" />
      </xsl:variable>

      <xsl:variable name="varDataType">
       <xsl:choose>
        <xsl:when test="@type='xs:int'">System.Int32</xsl:when>
        <xsl:when test="@type='xs:string'">System.String</xsl:when>
        <xsl:when test="@type='xs:dateTime'">System.DateTime</xsl:when>
        <xsl:when test="@type='xs:boolean'">System.Boolean</xsl:when>
       </xsl:choose>
      </xsl:variable>

      <Field Name="{$varFieldName}">
       <rd:TypeName><xsl:value-of select="$varDataType"/></rd:TypeName>
       <DataField><xsl:value-of select="$varFieldName"/></DataField>
      </Field>
     </xsl:template>

     <xsl:template match="xs:element" mode="HeaderTableCell">
      <xsl:variable name="varFieldName"> 
       <xsl:value-of select="@name" />
      </xsl:variable>

      <TableCell>
       <ReportItems>
        <Textbox Name="textbox{position()}">
         <rd:DefaultName>textbox<xsl:value-of select="position()"/>
         </rd:DefaultName>
         <Value><xsl:value-of select="$varFieldName"/></Value>
         <CanGrow>true</CanGrow>
         <ZIndex>7</ZIndex>
         <Style>
          <TextAlign>Center</TextAlign>
          <PaddingLeft>2pt</PaddingLeft>
          <PaddingBottom>2pt</PaddingBottom>
          <PaddingRight>2pt</PaddingRight>
          <PaddingTop>2pt</PaddingTop>
          <FontSize><xsl:value-of select="$mvarFontSize"/></FontSize> 
          <FontWeight><xsl:value-of select="$mvarFontWeightBold"/></FontWeight> 
          <BackgroundColor>#000000</BackgroundColor> 
          <Color>#ffffff</Color>
          <BorderColor>
           <Default>#ffffff</Default>
          </BorderColor>
          <BorderStyle>
           <Default>Solid</Default>
          </BorderStyle>
         </Style>
        </Textbox>
       </ReportItems>
      </TableCell>
     </xsl:template>

     <xsl:template match="xs:element" mode="DetailTableCell">
      <xsl:variable name="varFieldName"> 
       <xsl:value-of select="@name" />
      </xsl:variable>

      <TableCell>
       <ReportItems>
        <Textbox Name="{$varFieldName}">
         <rd:DefaultName><xsl:value-of select="$varFieldName"/></rd:DefaultName>
         <Value>=Fields!<xsl:value-of select="$varFieldName"/>.Value</Value>
         <CanGrow>true</CanGrow>
         <ZIndex>7</ZIndex>
         <Style>
          <TextAlign>Left</TextAlign>
          <PaddingLeft>2pt</PaddingLeft>
          <PaddingBottom>2pt</PaddingBottom>
          <PaddingRight>2pt</PaddingRight>
          <PaddingTop>2pt</PaddingTop>
          <FontSize><xsl:value-of select="$mvarFontSize"/></FontSize> 
          <FontWeight><xsl:value-of select="$mvarFontWeight"/></FontWeight> 
          <BackgroundColor>#e0e0e0</BackgroundColor> 
          <Color>#000000</Color> 
          <BorderColor>
           <Default>#ffffff</Default> 
          </BorderColor>
          <BorderStyle>
            <Default>Solid</Default>
          </BorderStyle>
         </Style>
        </Textbox>
       </ReportItems>
      </TableCell>
     </xsl:template>

     <xsl:template match="xs:element" mode="TableColumn">
      <TableColumn>
       <Width>0.75in</Width>
      </TableColumn>
     </xsl:template>

     <xsl:template name="replace-string">
      <xsl:param name="text"/>
      <xsl:param name="from"/>
      <xsl:param name="to"/>
      <xsl:choose>
       <xsl:when test="contains($text, $from)">
        <xsl:variable name="before" select="substring-before($text, $from)"/>
        <xsl:variable name="after" select="substring-after($text, $from)"/>
        <xsl:variable name="prefix" select="concat($before, $to)"/>
        <xsl:value-of select="$before"/>
        <xsl:value-of select="$to"/>
        <xsl:call-template name="replace-string">
         <xsl:with-param name="text" select="$after"/>
         <xsl:with-param name="from" select="$from"/>
         <xsl:with-param name="to" select="$to"/>
        </xsl:call-template>
       </xsl:when>
       <xsl:otherwise>
        <xsl:value-of select="$text"/>
       </xsl:otherwise>
      </xsl:choose>
     </xsl:template>
    </xsl:stylesheet>
于 2008-11-06T22:28:11.030 回答
6

将数据存储在 csv 文件中怎么样,这将为您提供查看选项。如果您的用户有 excel 或 Open Office Calc,他们可以轻松导入数据(不确定 Calc 是否有列限制,但 excel 2007 可以容纳 16384 列)并通过该程序查看?

于 2008-11-05T23:35:05.420 回答
3

你需要在一个表上查看多行吗?

我的猜测是这个数据是数字的,有什么办法可以将单行数据显示为 20*50 网格或类似的东西,然后只对行进行分页?

例如,第 1 行,第 1 列 = 数据库的第 1 列,第 2 行,第 1 列 = 数据库的第 21 列,等等

Id = 1
     1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
----|--------------------------------------------------------
  0 |  
 20 |  
 40 |
 60 |
 80 |
100 |
120 |
etc |
于 2008-11-05T23:24:43.337 回答
2

尝试使用非常小的字体的 html 表格

如果您担心格式化表格,请使用 CSS:

td { font-size: 0.2em; text-align: right; }

或者,如果您的所有数字大小相同,您也可以只生成“数字墙”显示,例如使用固定宽度的字体并在滚动面板中显示 5 个字符宽的列

于 2008-11-05T23:14:57.007 回答
1

这在一定程度上取决于它需要有多漂亮。如果这只是一个调试/抽查工具,您可以并排放置多个 DataGrid,每个 DataGrid 都显示一组列。会有点丑陋,但会是可行的。

OTOH,如果您需要一个半抛光的工具,您可能想提出一个自定义控件来处理它。基本上,您将加载正在查看的数据库部分,并带有一点缓冲区,当用户滚动当前加载的数据时,运行一个新查询。

于 2008-11-05T23:36:38.787 回答
1

DataGrid(甚至是 ListView)应该能够处理具有 32 列和 32 行的表,这将允许您一次显示整个 DB 行的数据。这将允许您立即查看某些单元格是否缺少数据。

于 2008-11-05T23:55:52.320 回答
0

即使提出这个建议我也觉得很脏,但你可以做一些类似的事情:

SELECT Field1 + ' - ' + Field2 + ... AS EvilMegaColumn FROM Table

但我真的认为这属于“如果你遇到这个限制,你做错了什么”的范畴。我真的看不出有任何理由、速度或其他需要 1000 列...

于 2008-11-05T23:16:57.517 回答
0

谁会读一个 1000 列的表???尝试想办法过滤或可视化数据。

于 2008-11-05T23:24:31.387 回答
0

也许您应该研究一种不同类型的数据库。我听说面向列的数据库非常适合这种事情(而典型的 RDBMS 是面向行的)。此外,如果您在第一次插入行后不返回更新行,那么二进制平面文件可能比巨型表更好?

于 2008-11-05T23:24:46.783 回答
0

我会把它作为一个向下钻取。在第一页(或页面顶部)中,您将拥有选择行的控件。在下一页(或页面底部)中,您将显示所选行中的数据。例如,根据所需的单元格宽度,您可以将其设置为 100 行 10 列或 1000 行 1 列。

作为动态客户端 javascript,这很容易做到——您甚至可以通过这种方式使其可编辑。我不确定这将如何在 C# 中工作。

于 2008-11-05T23:29:10.363 回答
0

如果您只是在验证之后,您是否可以不以编程方式检查每个字段并报告整行都可以!然后,您需要一个非常简单的数据网格,其中列出了不太好的行。

然后可以通过可以应用于单行的任何技术来检查它们,因为在大多数情况下您不需要浏览这些字段。我在这里假设您已经可以以某种方式查看整行,并且正在寻找一种同时浏览多行以查找丢失数据的方法(自动化这将使其更加可靠)。

于 2008-11-05T23:35:40.333 回答
0

从一个倾斜的角度来看,我会问用户是否需要一次“加载”所有列?

如果用户愿意一次显示列的子集(例如,一次显示 100 个,或者一次显示特定的集合),那么我会使用某种数据网格(内置的一个或一个ListView,或者第三方)来显示子集,CheckedListView 停靠在一边,允许显示感兴趣的子集。

或者,您能否显示某种汇总数据,显示 100 列组的计数/平均值/xxx?

于 2008-11-05T23:36:30.800 回答
0

我建议调查平面布局以外的其他内容。根据我的经验,数据库对列数和行字节大小有限制。

  • 您的 SQL 可能允许定义 1000 列。
  • SQL 行不能超过行字节限制。

每个数据库实现都有一个页面大小(4k / 8k),并且单行必须适合这个数据大小。NULL 通常是免费的。这意味着 1000 个整数 1000 x 4 字节将恰好适合 4k 页面大小。

如果您正在使用 varchars 谈论数据,那么问题会更糟。每列有多少个字符?可以填写多少列?如果您平均有 10 个字符,并且您的页面大小为 8k,那么您会因 SQL 错误而丢失数据。

如果你必须笑的话,但这种情况确实发生在一个特别长的打字员在一个平面数据表中,我知道这是在突破极限。

于 2008-11-06T05:43:00.513 回答
0

.. 以验证数据是否确实进入了那里。

可能它已经过时了,但是您可以使用像素图,其中单个像素代表表格的单个单元格(屏幕大于 1000)或 10 个单元格代表一个像素,单击时缩放区域。

像素的颜色将取决于数据。对于空/数据,它可能是黑色/白色。或者它可以是颜色来显示价值随着每一行的增加或减少。或红色表示数据突然跳跃。您可以在数据网格中正常捕捉到的所有异常。

然后,您只需要捕捉感兴趣区域中的点击坐标,并使用小表格来显示表格的该部分,而无需任何滚动。

只需单击即可返回像素图。

于 2009-01-13T00:36:24.897 回答
0

鉴于用户无论如何都必须水平滚动,您可以使用显示合理数量的列(例如,50)的常规数据网格。然后,您有一个位于网格下方的水平滚动条,用于选择要显示的列子集。当滚动条在左侧时,您会显示第 1-50 列,当您单击向右箭头时,您会转到 2-51 等。

这为您提供了滚动功能,而不必用数据重载网格控件。虽然您将失去在表格中自由光标或进行大型矩形选择的能力,但这听起来对这个应用程序来说不是一个问题。

于 2010-06-23T07:26:28.800 回答
-1

有多少数据对初始视图至关重要?我可以看到做一些类似于主/详细类型网格的操作,您将关键列(比如 10)放到数据网格上,当用户单击查看详细信息时,您可以获取剩余的列并将它们显示在“属性区”或这方面的东西。

于 2008-11-05T23:23:43.400 回答
-1

如果您只需要确保正在填充数据,那么为什么不让每一列都具有默认值,例如“void”、“blank”等。

然后,您可以在计算非默认值/总数时进行迭代以显示百分比。

现在您可以使用百分比值可视化数据完整性,甚至可以记录哪些列具有默认值(如列表/数组)以供进一步调查。

于 2008-11-05T23:39:31.167 回答
-1

您可能会考虑与您的用户群核对并查看他们真正需要查看的内容,然后为每个不同的需求设置视图,以使列倒计时。

另一种选择是读取数据,并从中创建一个巨大的静态 html 页面集。然后你可以从你的程序中调用浏览器来查看它。

于 2008-11-05T23:59:53.530 回答
-1

有一个可滚动的窗格并一次显示 10 列(可以主动加载或缓存这些列或您需要的任何内容)。向左滚动时,显示前十个。向右滚动时,显示后面的列序列。所以总而言之,在任何给定点只有 10 列处于活动状态。在我看来,以任何其他方式尝试实际显示 1000 列都是疯狂的。PS:这无非是一个理想的猜测;我不确定这是否远程可行。

于 2008-11-06T02:30:24.100 回答