2013-11-20 159 views
5

我的动机是将长ID数组作为参数传递给存储过程,并根据ID选择数据。所以我创建类型在SQL Server无效的对象名称'dbo.CategoryIdArray'

CREATE TYPE [dbo].[CategoryIdArray] AS TABLE(
[CategoryId] [bigint] NULL 
) 

GO 

和存储过程

ALTER PROCEDURE [dbo].[GetNewestArticleByCatsPageWise] 
    @dt as [dbo].[CategoryIdArray] READONLY, 
    @PageIndex INT = 1 
    ,@PageSize INT = 10 
    ,@PageCount INT OUTPUT 
AS 
BEGIN 
     SET NOCOUNT ON; 
     SELECT ROW_NUMBER() OVER 
      (
        ORDER BY [dateadded] 
      )AS RowNumber,[desid] 


INTO #Results 
    FROM [DB_user1212].[dbo].[discussions] as d , [DB_user1212].[dbo].[CategoryMap] as c where d.desid=c.[Topic Id] and c.[Category Id] in (select CategoryId from [dbo].[CategoryIdArray]) and [TopicType]='1' order by [dateadded] 

    DECLARE @RecordCount INT 
    SELECT @RecordCount = COUNT(*) FROM #Results 

    SET @PageCount = CEILING(CAST(@RecordCount AS DECIMAL(10, 2))/CAST(@PageSize AS DECIMAL(10, 2))) 
    PRINT  @PageCount 

    SELECT * FROM #Results 
    WHERE RowNumber BETWEEN(@PageIndex -1) * @PageSize + 1 AND(((@PageIndex -1) * @PageSize + 1) + @PageSize) - 1 

    DROP TABLE #Results 
END 

试过当我上面的功能异常运行时抛出Invalid object name 'dbo.CategoryIdArray'.被程序代码如下

public List<String> getNewestArticleByCategoryPageWise(long[] categoryId) 
    { 
     List<string> topicId= new List<string>(); 
     try 
     { 

     DataTable dt_Categories = new DataTable(); 
     dt_Categories.Columns.Add("Category", typeof(String)); 
     DataRow workRow; 
     foreach(long cat in categoryId) 
     { 
      workRow = dt_Categories.NewRow(); 
      workRow["Category"] = cat; 
      dt_Categories.Rows.Add(workRow); 
     } 
     int pageIndex = 1; 
      SqlCommand cmd = new SqlCommand("dbo.GetNewestArticleByCatsPageWise", con); 
      cmd.CommandType = CommandType.StoredProcedure; 
      cmd.Parameters.AddWithValue("@PageIndex", pageIndex); 
      cmd.Parameters.AddWithValue("@PageSize", 10); 
      cmd.Parameters.Add("@PageCount", SqlDbType.Int, 4).Direction = ParameterDirection.Output; 
      SqlParameter tvparam = cmd.Parameters.AddWithValue("@dt", dt_Categories); 
      tvparam.SqlDbType = SqlDbType.Structured; 
      con.Open(); 
      sdr= cmd.ExecuteReader(); 
     while(sdr.Read()) 
     { 
      topicId.Add(sdr.GetString(0)); 
     } 
      con.Close(); 
     } 
     catch(Exception ex) 
     { 
      con.Close(); 
      throw ex; 
     } 
     return topicId; 

    } 

使用上面的存储过程,但我创建它为类型。帮我看看我错过了什么。我接受this

+3

旧式的加入让我痛苦痛苦的眼泪。 – Hogan

+0

@霍根 - 过去 - 现在仍然是 - 最糟糕的绝对缩影。 – Brian

+0

@Hogan如何将我的旧式连接转换为更有效的连接 –

回答

4

问题在于存储过程中的这条线是用这条线

select CategoryId from [dbo].[CategoryIdArray]

我们不能像这种类型的选择,我们应该使用

select CategoryId from @dt

1

我并没有对有效的或正确的要求 - 但可读的现代语法的基本查询可以这样写:

SELECT ROW_NUMBER() OVER (ORDER BY [dateadded]) AS RowNumber,[desid] 
INTO #Results 
FROM [DB_user1212].[dbo].[discussions] as d 
JOIN [DB_user1212].[dbo].[CategoryMap] as c ON d.desid=c.[Topic Id] 
JOIN [dbo].[CategoryIdArray] arr ON c.[Category Id] = arr.CategoryID 
WHERE [TopicType]='1' 
+0

它可以这样写,但问题仍然是无法回避。 –

+0

@HotCoolStud - 你试过'[DB_user1212]。[dbo]。[CategoryIdArray]'?你究竟命名了什么?这只是一个命名/命名空间问题。 – Hogan

+0

我用与上面写的相同的方式命名它,当我尝试[DB_user1212]。[dbo]。[CategoryIdArray]错误出现类型名'DB_user1212.dbo.CategoryIdArray'包含超过最大数量的前缀。最大值为1. –

0

相反的:

workRow["Category"] = cat; 

使用

workRow["CategoryId"] = cat; 
+0

好点 - 这一行也必须改变:'dt_Categories.Columns.Add(“Category”,typeof(String));' – Hogan

+0

我尝试过,但它dosn't我不出。 –

1

这里是您的解决方案:

在您的存储过程,在WHERE语句中,您是从选择* “TYPE”而不是传入的实际参数对象。它就像做“SELECT * FROM VARCHAR”,这没有任何意义。试试这个:

... 
and c.[Category Id] in (
    select CategoryId from @dt -- select from the actual parameter, not its TYPE 
) 
... 
4

当我遇到这些问题时,我做的第一件事就是创建一个示例数据库。下面的代码创建以下内容。

1 - 数据库名为[测试]

2 - 表名为[讨论]

3 - 表名为[CategoryMap]

4 - 用户定义的表类型名为[CategoryIdArray]

5-装载带有100条记录的数据的表格

-- 
-- Create a test db 
-- 

USE [master]; 
go 

CREATE DATABASE [Test]; 
GO 

-- 
-- Create the user defined type 
-- 

USE [Test]; 
go 

CREATE TYPE [CategoryIdArray] AS 
TABLE 
(
[CategoryId] [bigint] NULL 
); 



-- 
-- Create skelton tables 
-- 

create table Discussions 
(
    dis_id int identity (1,1), 
    dis_name varchar(64), 
    dis_added_dte datetime default getdate() 
); 
go 

create table CategoryMap 
(
    cat_id int identity(1,1), 
    cat_topic_id int, 
    cat_topic_type char(1) 
); 
go 

-- clear tables 
truncate table Discussions; 
truncate table CategoryMap; 
go 


-- 
-- Create 100 rows of dummy data 
-- 

declare @cnt int = 0; 
while @cnt < 100 
begin 
    insert into Discussions (dis_name) 
    values ('sample discussion record # ' + str(@cnt, 2, 0)); 

    insert into CategoryMap (cat_topic_id, cat_topic_type) 
    values (@cnt+1, '1') 

    set @cnt = @cnt + 1; 
end; 
go 


-- 
-- Show the sample data 
-- 

select * from Discussions; 
go 

select * from CategoryMap; 
go 

第二步是重新编写存储过程。如果您在2012年以下使用,请使用窗口函数rownumber()。在2012年,订单的偏移和获取子句包含在分页中。

http://technet.microsoft.com/en-us/library/ms188385(v=sql.110).aspx

-- 
-- Create my procedure 
-- 

create procedure [GetArticlesByPage] 
    @Tvp as [CategoryIdArray] READONLY, 
    @PageIndex INT = 1, 
    @PageSize INT = 10, 
    @PageCount INT OUTPUT 
AS 
BEGIN 

    -- Declare variables 
    DECLARE @var_recs int = 0; 
    DECLARE @var_offset int = 0; 

    -- Do not count the records 
    SET NOCOUNT ON; 

    -- Start of paging 
    SET @var_offset = @var_offset + ((@PageIndex - 1) * @PageSize); 

    -- Set page count variable 
    SELECT @var_recs = count(*) 
    FROM 
     [dbo].[Discussions] as d 
    JOIN 
     [dbo].[CategoryMap] as c 
    ON 
     d.dis_id = c.cat_topic_id 
    JOIN 
     @TVP a 
    ON 
     c.cat_id = a.CategoryId 
    WHERE 
     cat_topic_type = '1'; 

    set @PageCount = ceiling(cast(@var_recs as real)/cast(@PageSize as real)); 


    -- 
    -- Return the record set 
    -- 

    SELECT 
     dis_id 
    FROM 
     [dbo].[Discussions] as d 
    JOIN 
     [dbo].[CategoryMap] as c 
    ON 
     d.dis_id = c.cat_topic_id 
    JOIN 
     @TVP a 
    ON 
     c.cat_id = a.CategoryId 
    WHERE 
     cat_topic_type = '1' 
    ORDER BY 
     dis_added_dte 
    OFFSET @var_offset ROWS 
    FETCH NEXT @PageSize ROWS ONLY; 

END; 
GO 

我没有留在原地的页数;但是,我不认为这是必要的,因为您可以重复呼叫,直到结果集为空。

请不要设置转储到一个临时表中的记录,因为它可能是相当大的,如果你是返回所有要显示的列。我选择了两个独立的电话。一个总数。一个用于单个页面。

最后一个TSQL部分是从SSMS测试存储过程。

-- 
-- Call the stored procedure 
-- 

-- instantiate tvp 
DECLARE @my_tvp as [CategoryIdArray]; 
DECLARE @my_page_cnt as int; 

-- add 25 entries 
declare @cnt int = 25; 
while @cnt < 50 
begin 
    insert into @my_tvp (CategoryId) 
    values (@cnt + 1); 
    set @cnt = @cnt + 1; 
end; 

-- show the data in the tvp 
select * from @my_tvp 

-- call the function 
exec [GetArticlesByPage] @my_tvp, 1, 10, @PageCount = @my_page_cnt OUTPUT; 

-- show the data in the output 
select @my_page_cnt as 'my_pages'; 

go  

在我的测试示例中,我想将行26到50分页为10行。结果1是25行,结果2是被分页的10行,结果3是多少页。因此,解决方案的TSQL部分是完善的。

enter image description here

请继续关注今晚稍后C#程序调试会话。

http://www.mssqltips.com/sqlservertip/2112/table-value-parameters-in-sql-server-2008-and-net-c/

看看这篇文章。它正在做你正在做的事情。

这里有一些想法去尝试。

1 - 确保连接属性,登录的默认数据库[测试]我的例子。

2是在[Test]数据库中定义的类型吗?请仔细检查一下。

3 - 这是正确的吗?列名称是数据库类型中的[CategoryId]。你有以下 - [类别]。尝试更改C#代码中的名称。

dt_Categories.Columns.Add( “类别” 的typeof(字符串));

4 - 删除[dbo]。来自SP中的类型。它不在MS SQL Tips的示例中。可能会混淆这个问题。 SQL服务器将解析名称。

5 - 我注意到类型被定义为大INT但表中的ID为int?确保数据类型一致。

请尝试这些建议。回到我身上,看看你是怎么做出来的。

如果这仍然是一个问题,你能否给我一个详细的调用堆栈跟踪和错误消息?

因此,这里是我答应了C#控制台应用程序。

它按预期工作。

你在混合一些构思ADO.NET和数据表的基础。你应该习惯看直接窗口和局部变量。这将帮助您追踪问题。

这是我对存储过程的示例调用。

1 - 设置数据表(50到74)

2 - 页面由5的

3中的数据 - 看第二页

// 
// Good Ref. - http://msdn.microsoft.com/en-us/library/ms254937(v=vs.110).aspx 
// 

// Basic stuff from C# console app 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

// Required for data table 
using System.Data; 
using System.Data.SqlClient; 

// Standard stuff ... 
namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      // Debug info 
      Console.WriteLine("Test - Start"); 

      // Create the table with one column 
      DataTable my_Table; 
      my_Table = new DataTable("Category"); 
      my_Table.Columns.Add("CategoryId", typeof(string)); 

      // Add data to table 
      for (int my_Cnt = 50; my_Cnt < 75; my_Cnt++) 
      { 
       DataRow my_Row = my_Table.NewRow(); 
       my_Row["CategoryId"] = my_Cnt.ToString(); 
       my_Table.Rows.Add(my_Row); 
      } 

      // Debug info 
      Console.WriteLine("Test - created data set"); 

      // Create a connection 
      SqlConnection my_Conn; 
      string str_Conn = "Server=localhost;Database=Test;Trusted_Connection=True;"; 
      my_Conn = new SqlConnection(str_Conn); 

      // Debug info 
      Console.WriteLine("Test - create connection"); 

      // Create the command and set its properties. 
      SqlCommand my_Cmd = new SqlCommand(); 
      my_Cmd.Connection = my_Conn; 
      my_Cmd.CommandText = "dbo.GetArticlesByPage"; 
      my_Cmd.CommandType = CommandType.StoredProcedure; 

      // Add parameter 0 
      SqlParameter my_Parm0 = new SqlParameter(); 
      my_Parm0.ParameterName = "@Tvp"; 
      my_Parm0.SqlDbType = SqlDbType.Structured; 
      my_Parm0.Direction = ParameterDirection.Input; 
      my_Parm0.Value = my_Table; 
      my_Cmd.Parameters.Add(my_Parm0); 

      // Add parameter 1 
      SqlParameter my_Parm1 = new SqlParameter(); 
      my_Parm1.ParameterName = "@PageIndex"; 
      my_Parm1.SqlDbType = SqlDbType.Int; 
      my_Parm1.Direction = ParameterDirection.Input; 
      my_Parm1.Value = 2; 
      my_Cmd.Parameters.Add(my_Parm1); 

      // Add parameter 2 
      SqlParameter my_Parm2 = new SqlParameter(); 
      my_Parm2.ParameterName = "@PageSize"; 
      my_Parm2.SqlDbType = SqlDbType.Int; 
      my_Parm2.Direction = ParameterDirection.Input; 
      my_Parm2.Value = 5; 
      my_Cmd.Parameters.Add(my_Parm2); 

      // Add parameter 3 
      SqlParameter my_Parm3 = new SqlParameter(); 
      my_Parm3.ParameterName = "@PageCount"; 
      my_Parm3.SqlDbType = SqlDbType.Int; 
      my_Parm3.Direction = ParameterDirection.Output; 
      my_Parm3.Value = 5; 
      my_Cmd.Parameters.Add(my_Parm3); 

      // Open the connection 
      my_Conn.Open(); 

      // Debug info 
      Console.WriteLine("Test - execute reader"); 

      // Execute the reader 
      SqlDataReader my_Reader = my_Cmd.ExecuteReader(); 
      if (my_Reader.HasRows) 
      { 
       while (my_Reader.Read()) 
       { 
        Console.WriteLine("{0}", my_Reader[0].ToString()); 
       } 
      } 
      else 
      { 
       Console.WriteLine("No rows found."); 
      } 

      // Close the reader 
      my_Reader.Close(); 

      // Number of pages (output after reader - order is important) 
      Console.WriteLine("Pages = "); 
      Console.WriteLine(my_Cmd.Parameters["@PageCount"].Value.ToString()); 

      // Close the connection 
      my_Conn.Close(); 

      // Debug info 
      Console.WriteLine("Test - close connection"); 

      // Debug info 
      Console.WriteLine("Test - End"); 

      // Pause to view output 
      Console.Read(); 
     } 
    } 
} 

这里是正确的输出的快照来自C#控制台应用程序。

enter image description here

我要感谢您的提问!

我用C#编码已经有一段时间了。但是就像一辆自行车,不需要很长时间就可以重新开始。 T-SQL示例使用SSMS 2012完成,C#程序使用VS 2013完成。最新和最好的。

好少!

+0

非常好的解决方案,但没有人蝙蝠眼睛。哈哈努力。 – user2705620

0

如果用户将默认数据库设置为您尝试访问的数据库,请检入SQL Server Management Studio。我有相同类型的错误,并陷入了数天。最后发现用户已将Master设置为其默认数据库。