【问题标题】:How can you name the Dataset's Tables you return in a stored proc?如何命名在存储过程中返回的数据集表?
【发布时间】:2010-10-10 01:14:45
【问题描述】:

我有以下存储过程

Create procedure psfoo ()
AS
select * from tbA
select * from tbB

然后我以这种方式访问​​数据:

     Sql Command mySqlCommand = new SqlCommand("psfoo" , DbConnection)
     DataSet ds = new DataSet();
     mySqlCommand.CommandType = CommandType.StoredProcedure;
     SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter();
     mySqlDataAdapter.SelectCommand = mySqlCommand;
     mySqlDataAdapter.Fill(ds);

现在,当我想访问我的表时,我必须这样做:

     DataTable datatableA = ds.Tables[0];
     DataTable datatableB = ds.Tables[1];

数据集 Tables 属性也通过字符串(而不是 int)获得访问器。

是否可以在 SQL 代码中指定表的名称,以便我可以改为这样写:

     DataTable datatableA = ds.Tables["NametbA"];
     DataTable datatableB = ds.Tables["NametbB"];

我使用的是 SQL Server 2008,如果这会有所不同的话。

【问题讨论】:

  • 很好的问题。太糟糕了,从答案来看,这似乎是不可能的。能够为过程中的查询提供别名并将其用作生成的 DataTable 的 TableName 将是一个很好的功能。不得不依赖在过程中执行查询的顺序很糟糕,因为将来有人可能会修改过程并在不知不觉中破坏您的代码。
  • @Jim 看到 Ramzan 的回答。除了让 Microsoft 添加从 SP 命名表的功能之外,这似乎是可行的方法,尤其是如果您像我的团队那样将数据访问代码包装在实用程序类中。

标签: .net sql sql-server dataset


【解决方案1】:

据我所知,从存储过程中,您不能这样做。但是,您可以在检索到 DataSet 后设置名称,然后从那时起使用它们。

ds.Tables[0].TableName = "NametbA";

【讨论】:

  • 谢谢,我懒得打开VS :)
  • 你也可以像下面的 ds.Tables["OldNameOfTable"].TableName = "NewNameOfTable";
【解决方案2】:

填完DataSet后有什么原因不能手动命名?

mySqlDataAdapter.Fill(ds);
ds.Tables[0].TableName = "NametbA";
ds.Tables[1].TableName = "NametbB";

我不知道如何命名作为存储过程的多个结果集的一部分返回的 DataTables,但如果您知道存储过程返回的内容,那么手动命名它们应该可以正常工作。

编辑

知道您可以控制存储过程,一种替代方法可能是向结果集中添加一个表示表名的列。然后你也许可以做类似的事情:

foreach (DataTable table in ds.Tables)
{
    table.TableName = table.Rows[0]["TableName"].ToString();
}

但是,这依赖于从实际包含行的存储过程返回的结果集。如果它们不包含行,那么您必须将其包装在“if”语句中,并且并非每个表都会获得名称。

【讨论】:

  • 是的。我正在考虑编写一个存储过程,它并不总是返回相同的表,具体取决于用户的高度。由于返回的表可能会有所不同,我想使用强名称而不是索引来访问它们。
  • 我的替代解决方案是返回空占位符,以便我可以依赖索引。但是感觉不对:(
  • 是的,在每个“真实”结果集之间返回“占位符”表格以给下一个结果集命名可能是唯一的解决方案,但我同意这感觉有点难看。
  • 仍然丑陋,但可能更少:返回一个表(第一个),其中包含以下表的每个表名的一行。
【解决方案3】:

这对我有用,但需要一些额外的工作才能获得预期的表名:

    Dim tableCount As Integer = 3
    Dim tables(tableCount) As DataTable
    tables(0) = (New DataTable("Employee"))
    tables(1) = (New DataTable("Manager"))
    tables(2) = (New DataTable("Department"))
    dsUControlData.Tables.Add(tables(0))
    dsUControlData.Tables.Add(tables(1))
    dsUControlData.Tables.Add(tables(2))

    'Fill required tables
    da.Fill(0, 0, tables)

    Return dsUControlData

【讨论】:

    【解决方案4】:

    好主意,最好你的过程返回三个结果集而不是在上添加表名

    firs trow of each table
    select 'tbA' FirstTableName,'tbB' SecondTableName
    select * from tbA
    select * from tbB
    

    每次执行过程时,表 [0] 都会有一行继续选择表名

    另一个想法是传递表名作为过程的输出参数

    Create procedure psfoo (@tb1 varchar(50) output,@tb2 varchar(50) output)
    AS
    set @tb1='tbA'
    set @tb2 'tbB'
    select * from tbA
    select * from tbB
    

    如果这种方法限制了你并且选择是可变的,你可以像这样创建过程

    Create procedure psfoo ()
        AS
        select * from tbA
        select * from tbB
    return 'tbA,tbB'
    

    并通过将过程返回值拆分为字符串[] 您可以按顺序获取表的名称

    returnvalue=@returnvaluefromSp.split(new char[]{','});
    string tablenam1=returnvalue[0];
    string tablenam2=returnvalue[1];
    

    【讨论】:

    • 这里提出的答案都是为了 C# 代码是通用的并接受任何存储过程。这正是我想要的!
    • 据我所知return 'tbA,tbB' 不起作用,因为 SQL Server 存储过程只能在返回语句MSDN Article 中返回一个整数。所以我认为输出变量可能是我的解决方案!
    【解决方案5】:

    您可以返回一个额外的结果集,像这样命名表:

    SELECT 'firstTableName' AS tbl
    UNION
    SELECT 'secondTableName' AS tbl
    UNION
    ...
    

    如果这是第一个结果集,您可以使用它来命名后续结果集。

    【讨论】:

      【解决方案6】:

      我有一个存储过程,可以访问我企业中的所有数据并返回多个结果集。在每个结果集之前使用名称进行选择对我来说更有意义。 像这样:

      SELECT 'Customers' AS TableName
      SELECT ID, Name FROM dbo.Customer
      SELECT 'Orders' AS TableName
      SELECT ID, Created FROM dbo.[Order]
      

      然后,当我遍历 MyDataSet.Tables 时,我只有一个循环和直接的查找逻辑。没有要跟上的主元数据表。这甚至在没有返回表的情况下也有效。如果不返回表数据,则不返回匹配的元数据,这对我来说很有意义。

      For Each DataTable As DataTable In MyDataSet.Tables
        MyDataSet.Tables(MyDataSet.Tables.IndexOf(DataTable) + 1).TableName = DataTable.Rows(0)("TableName")
      Next
      

      如果我想在元数据表中添加更多列,我可以。

      【讨论】:

      • 我觉得这是唯一可以让您确定表是否丢失或移动的解决方案。如果某些开发人员交换了 2 个表格订单并且您使用了此问题中的任何其他答案,那么您的表格是错误的。此解决方案将允许您检查表。我想这将是我最终使用的。
      【解决方案7】:

      存储过程:

          select 'tbA','tbB','tbC' 
          select * from tbA
          select * from tbB
          select * from tbC
      

      前端:

             int i = 1;
             foreach (string tablename in dsEmailData.Tables[0].Rows[0][0].ToString().Split(','))
             {
                 dsEmailData.Tables[i++].TableName = tablename;
             }
      

      希望对你有帮助

      【讨论】:

      • 天才!谢谢!
      • 这可能并不总是有效(所以这是一个错误的答案恕我直言),在某些情况下,任何表可能不仅返回 0 结果,而且表本身丢失。在我的情况下返回 XML(或没有表格的字符串)。
      【解决方案8】:

      我知道这是一篇很老的帖子,但要回答... 是的,这是可能的。 只需在数据适配器Fill 调用中添加"TableName"..

      MyDataAdapter.Fill(ds, "TableName");  
      

      我是 C# 人,但你明白了。

      【讨论】:

      • 这会将交易分解成碎片,这是我们需要避免的事情。他问这个问题的原因是他想把它保留在一笔交易中,我想。
      【解决方案9】:

      我知道这是一个老问题,但我今天正在考虑做同样的事情,所以我不只是循环遍历结果数据集的索引。因为如果存储过程按照 select 的顺序发生变化,.net 代码就会崩溃。

      我想出了用表名向结果集中添加一列并使用 case 语句查看该列是否存在于表中的解决方案。但似乎我在移动代码。刚刚使 SQL 静态化。另外,如果您在不同的表中有一个名称相同的列怎么办。跛脚...

      myDS.Tables(i).Columns.Contains("TableName")
      

      【讨论】:

        【解决方案10】:

        这可能是解决办法

         Create procedure psfoo ()
         AS
         select * ,'tbA' as TableName from tbA
         select * ,'tbB' as TableName from tbB
        

        然后在C#代码中

             foreach (DataTable dt in ds.Tables)
                {
                    if (dt.Rows[0]["TableName"].ToString().Contains("tbA"))
                    {
        
                    }
                    else if (dt.Rows[0]["TableName"].ToString().Contains("tbB"))
                    {
        
                    }
                }
        

        【讨论】:

          【解决方案11】:

          为了给数据集中的所有表命名, 我们需要在 SP 或 T-Sql 的最后再写一个选择查询。

              lsqry = New System.Text.StringBuilder
          lsqry.AppendLine("select * from Bill_main")
          lsqry.AppendLine("select * from serviceonly")
          lsqry.AppendLine("select * from PendingService")
          lsqry.AppendLine("select * from T_servicebillhd")
          lsqry.AppendLine("select 'Bill_Main','serviceonly','PendingService','T_servicebillhd'")
          
          Using con As New SqlConnection(lsConnection)
              Try
                  con.Open()
                  Using cmd As SqlCommand = con.CreateCommand
                      With cmd
                          .CommandText = lsqry.ToString
                          .CommandType = CommandType.Text
                      End With
                      Using da As New SqlDataAdapter(cmd)
                          da.Fill(DS)
                      End Using
                  End Using
          
                  For li As Integer = 0 To DS.Tables.Count - 2
                      DS.Tables(li).TableName = DS.Tables(DS.Tables.Count - 1).Rows(0).Item(li)
                  Next
              Catch ex As Exception
          
              End Try
          End Using
          

          在上面的例子中,我使用的是 t-sql 语句而不是 SP。 我在其中写了 4 个选择查询,最后一个是这些表的表名。 并获取包含表名称的最后一个表,并使用 TableName 属性将其分配给表。

          【讨论】:

            【解决方案12】:

            你可以试试这个:

            在您的程序中添加以下行

            Select 'TableName1','TableName2';
            

            使用 C#:

            for (var i=0;i<ds.Tables[0].Columns.Count;i++)
             {
                ds.Tables[i + 1].TableName = ds.Tables[0].Columns[i].ColumnName;
            
              }
            

            【讨论】:

            • 正如我上面评论的那样,任何表都可能不存在。
            【解决方案13】:

            试试这个

            YourDataAdapter.Fill(ds, "Table");
            

            【讨论】:

              【解决方案14】:

              试试这个。在 MS SQL 2008 R2 中,我总是使用这个。

              mySqlDataAdapter.Fill(ds,"NameTableA");
              

              【讨论】:

                【解决方案15】:

                应该使用select into "yourtablename" from originaltable,这只是一个例子,找到select into语句的正确语法。

                这是满足您要求的正确方法。谢谢。

                【讨论】:

                  【解决方案16】:

                  这个问题是老问题了,但是,它在谷歌搜索的顶部,呵呵。 也许它会帮助某人......

                  答案是肯定的,可以在代码中指定表的名称,这样就可以通过名称访问DataSet中的DataTable。

                  首先,我必须解释一下 TableMapping 的工作原理。 如果我们不使用 SqlDataAdapter 指定 TableMappings(SqlDataAdapter 将填充我们的 DataSet),那么默认情况下第一个表将命名为“Table”,第二个将命名为“Table1”,第三个将命名为“Table2”等。

                  所以,当我们想在 DataSet 中命名 DataTable 时,我们这样使用它:

                  //...
                  
                  System.Data.DataSet myDataSet = new System.Data.DataSet();
                  
                  using (System.Data.SqlClient.SqlDataAdapter dbAdapter = new System.Data.SqlClient.SqlDataAdapter(dbCommand))
                  {
                      dbAdapter.TableMappings.Add("Table", "MyFirstTitleForTableOne");
                      dbAdapter.TableMappings.Add("Table1", "MyFirstTitleForTableTwo");
                      dbAdapter.TableMappings.Add("Table2", "MyFirstTitleForTableThree");
                      dbAdapter.TableMappings.Add("Table3", "MyFirstTitleForTableFour");
                          //...
                  
                      dbAdapter.Fill(myDataSet);
                  }
                  
                  //...
                  

                  而且,现在我们可以通过标题访问 DataTable:

                  System.Data.DataTable firstTable = myDataSet.Tables["MyFirstTitleForTableOne"];
                  System.Data.DataTable secondTable = myDataSet.Tables["MyFirstTitleForTableTwo"];
                  

                  抱歉,我没有编写检查 null (myDataSet) 或将其设置在 try/catch 块中的代码,因为我认为这与这个问题无关。

                  【讨论】:

                    【解决方案17】:

                    我知道这是旧帖子,但是当我的存储过程返回多个数据集时,我也遇到过类似的情况,我不确定根据满足的条件将返回多少个数据集。

                    所以我实现了如下排序以按特定顺序获取返回数据集,这样我也可以根据代码端提供的条件在 C# 代码上管理每个数据集。

                    if (dsop != null && dsop.ReturnDataSet != null && dsop.ReturnDataSet.Tables.Count > 0)
                    {
                                foreach (DataTable dt in dsop.ReturnDataSet.Tables)
                                {
                                    if (dt.Rows.Count > 0 && dt.Columns.Count > 1)
                                    {
                                        foreach (DataRow dr in dt.Rows)
                                        {            //Alarm table[0]
                                            if (dt.TableName == "Table")
                                            {
                                                OperatorHistory operatorReportEntity = new peratorHistory
                                                {                                   
                                                };
                                                reportdata.Add(operatorReportEntity);
                                            }
                                            //ARC table[2], DW table[3], GT table[4]
                                            else if (dt.TableName == "Table2" || dt.TableName == "Table3" || dt.TableName == "Table4")
                                            {
                                                OperatorHistory operatorReportEntity = new peratorHistory
                                                {                                   
                                                };
                                                reportdata.Add(operatorReportEntity);
                                            }
                                       }    
                                   }           
                                 }  
                      }
                    
                    --table[0]
                    if(LEN(@ADEventIdsForAlarms ) > 1)
                    select * from (
                    ---my query logic here
                    ) as TBL_ADEventIdsForAlarms
                    else
                    select 'TBL_ADEventIdsForAlarms'
                    
                    --table[1]
                    --/* Just Message  */ UNION ALL
                    if(LEN(@ADEventIdsForOPR ) > 1)
                    ---my query logic here
                    ) as TBL_ADEventIdsForOPR
                    else
                    select 'TBL_ADEventIdsForOPR'
                    
                    --table[2]
                    if(LEN(@ADEventIdsForARC) > 1)
                    select * from (
                    ---my query logic here
                    ) as TBL_ADEventIdsForARC
                    else
                    select 'TBL_ADEventIdsForARC'
                    

                    【讨论】:

                      猜你喜欢
                      • 2011-02-17
                      • 1970-01-01
                      • 1970-01-01
                      • 2016-08-19
                      • 1970-01-01
                      • 2020-03-27
                      • 1970-01-01
                      • 2016-04-09
                      • 2011-03-01
                      相关资源
                      最近更新 更多