SQL子查询和CAST无法正常工作

时间:2013-05-07 08:55:15

标签: asp.net sql-server sql-server-2008 join

我试图从varchar形式的一个表中获取数据,并将其传递给与int具有相同字段的其他查询。

Article包含以下字段

(ArticleID int, ArticleTitle nvarchar(200), ArticleDesc nvarchar(MAX))

示例数据

1 Title1 Desc1
2 Title2 Desc2
3 Title3 Desc3

我有另一个名为Banner的表,其中包含与文章相关的横幅

(BannerID int, BannerName nvarchar(200), BannerPath nvarchar(MAX), ArticleID varchar(200))

样本数据

**BannerID BannerName BannerFile ArticleID**
    100 Banner1 BannerPath1 '1','3','5'
    101 Banner2 BannerPath2 '2','3','5'
    102 Banner3 BannerPath3 '8','3','5'
    103 Banner4 BannerPath4 '10','30','5','2','3','5'

示例查询

SELECT ArticleTitle 
FROM Article 
WHERE CAST(ArticleID AS varchar(200)) IN (
    SELECT  ArticleID FROM Banner WHERE BannerID = 2
)

在我的实际项目中,我在横幅表中有多个字段,以便我可以分配横幅文章,作家,类别,页面..  出于这个原因,我决定将ArticleID或WriteID或CatID存储为此格式的单个字段'10','30','5','2','3','5'

我改变了我的结构然后我可能会为一个横幅创建数百条记录,并且可以将一个横幅分配给这个article, writer, Category, Pages中的任何一个

下面的查询返回零行可能是我的演员正在创建问题我会很感激我如何在不改变我的数据库结构的情况下解决这个问题

SELECT ArticleTitle FROM Article WHERE CAST(ArticleID AS varchar(200)) IN (SELECT  ArticleID FROM Banner WHERE BannerID = 2)

更新:

我决定坚持我的设计没有任何违法行为,因为我提出的问题是网站的支持报告部分,这些部分不会经常使用。关于不规范表格我可能是错的......

我的实际情况假设用户访问网址

`abc.com/article/article.aspx?articleID=30&CatID=10&PageID=3&writerID=3`

根据此网址,我可以使用UNION运行四个查询以获取所需的横幅广告,我必须决定横幅优先级,所以我会像这样做

     `SELECT  BannerName, BannerImage FROM Banner WHERE ArticleID LIKE '%''30''%'`
    UNION ALL
    `SELECT  BannerName, BannerImage FROM Banner WHERE CategoryID LIKE '%''10''%'`
     UNION ALL
     `Another query .......`

如果我这样做,那么查询将必须在单个表中查找具有少量行的横幅但是如果我基于JW对表进行规范化,这是一种很好的方法,这可能导致30-40行不同表格中的每个横幅可能影响表现,因为我必须为新文章添加新横幅(针对新杂志问题)。

我知道我违反了规范化的所有规律,但我担心我必须为了表现而这样做,因为我可能最终每100个横幅有2000行。这将随着时间的推移而增长。

再次更新

我希望这张图片能让您了解我想要做的事情

enter image description here

如果我这样做,那么我每个横幅只需要1行&如果我进一步规范化并创建更多表格,那么我可能最终会为一个横幅设置几行 从横幅表中取出上面的图像样本,然后我的第一个横幅将有27行 第二横幅11排 三分旗14行。

为了避免这种情况,我想到在他们受尊重的字段中存储多个articleID,IssueID,PageID ....这种方法可能很脏但是有效。

我肯定有一些反馈,从他们的角度来看是可以理解的..因为我提供了进一步的细节,我的方法完全不专业或者很好,记住网站可能有非常好的流量和&这种方法可能会更快。

3 个答案:

答案 0 :(得分:6)

当您在搜索记录时使用这些值时,在列中保存逗号分隔值时,这是一个非常糟糕的设计。

您需要将表格正确规范化并将其重组为3表设计,因为我可以在Many-to-ManyArticle上看到Banner关系。

建议的架构设计:

文章表

  • ArticleID(PK)
  • ArticleTitle
  • ArticleDesc

横幅表

  • BannerID(PK)
  • BannerName
  • BannerPath

Article_Banner表

  • ArticleID(FK)(也是具有BannerID的复合PK)
  • BannerID(FK)

通过这种设计,您可以简单地查询您的记录,如:

SELECT  a.*
FROM    Article a
        INNER JOIN Article_Banner b
            ON a.ArticleID = b.ArticleID
WHERE   b.BannerID = 2

结构的优点:

  • 可以轻松创建查询语句
  • 可以利用已定义的索引
  • 等。

答案 1 :(得分:3)

另外,对于John Woo's excellent answer,我将尝试回答“为什么查询不返回任何结果”这一问题。

我将不考虑WHERE b.BannerID = 2条款,任何样本记录显然都不符合这条款。

查询的主要问题是IN clause。 IN将告诉您是否在一组项目中找到了一个项目。你期望它做的是迭代一组集合并告诉你是否找到了该项目。

为了说明这一点,这里有两个简化的查询:

-- this will print 0
if '1' in ('''1'',''3'',''5''')
    print 1
else 
    print 0

-- this will print 1
if '1' in ('1', '3', '5')
    print 1
else 
    print 0

重点是IN是基于集合的操作,而不是找到子字符串的字符串函数。

问题的一个可能解决方案是使用CHARINDEX来执行子字符串检测:

select ArticleTitle 
from Article a
join Banner b
  on charindex(CAST(a.ArticleID AS varchar(200)), b.ArticleID) > 0

此版本不正确,因为搜索ID '1'也会匹配'11','12'等值。

为了获得正确的结果,您最终可能会得到类似于此的查询(为了确保您只匹配星号之间的值):

select ArticleTitle 
from Article a
join Banner b
  on charindex('''' + CAST(a.ArticleID AS varchar(200)) + '''', b.ArticleID) > 0

SQLFiddle:http://www.sqlfiddle.com/#!3/2ee3c/23

但是,此查询有两大缺点:

  • 对于相对较大的表来说速度非常慢,因为它无法使用任何索引,需要扫描Banner
  • 中每行的Article
  • 代码变得有点复杂,你添加的功能越多,就越难理解它,导致可维护性问题。

这两个问题都是你做错了什么的气味。遵循JW的解决方案将摆脱这两个问题。

答案 2 :(得分:0)

完全同意上面的例子很糟糕,而不是他正在做的事情的正确方法。但是,根错误消息问题仍然存在,并且在某些情况下是一个问题。

我的情况是使用表来保存一些自定义表单字段元素数据。在没有布置整个结构的情况下,我将只展示展示和重现问题所需的内容。在这种情况下,我还可以确认问题是围绕IsNumeric解决的。结合SubQueries也是如此。该示例包含两个项目,项目名称模拟自定义字段元素/类型和字段值。有些是名字,有些是劳动分钟。可以是权重,临时值,距离等等,它是客户可定义的额外数据。

Create Table dboSample (cKey VarChar(20), cData VarChar(50))

Insert Into dboSample (cKey, cData) Values ('name', 'Jim')
Insert Into dboSample (cKey, cData) Values ('name', 'Bob')
Insert Into dboSample (cKey, cData) Values ('labortime', '60')
Insert Into dboSample (cKey, cData) Values ('labortime', '00')
Insert Into dboSample (cKey, cData) Values ('labortime', '15')

Select * From (Select * From dboSample Where IsNumeric(cData) = 1) As dboSampleSub Where Cast(cData As Int) > 0

导致错误“转换varchar值时转换失败' Jim'数据类型为int。“

较低的嵌套查询有一个where子句,将返回的行限制为仅包含基于数字的数据。但是,较高级别的强制转换显然会看到子查询返回中未包含的行。它实际上是查看和处理较低嵌套查询的数据。无法找到Select OPTION标志以防止这种情况发生。