1

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

Article具有以下字段的表

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

样本数据

1 Title1 Desc1
2 Title2 Desc2
3 Title3 Desc3

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

(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)

更新:

对我决定坚持我的设计的任何人都没有冒犯,因为我提出的问题是针对网站的支持报告部分,该部分不会经常使用。关于不规范化表格我可能是错的......

我的实际场景假设用户访问url

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

基于这个 url,我可以运行四个查询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 行,而且这会随着时间的推移而增长。

再次更新

我希望这张图片能让您全面了解我正在尝试做的事情

在此处输入图像描述

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

为了避免这种情况,我想在他们尊重的字段中存储多个 articleID、IssueID、PageID ....。这种方法可能很脏,但它正在工作。

我肯定有一些-ve 反馈,从他们的角度来看是可以理解的。由于我提供了更多详细信息,因此我的方法完全不专业,或者请记住,网站可能有很好的流量并且这种方法可能更快。

4

3 回答 3

6

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

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

建议的架构设计:

文章表

  • 文章 ID (PK)
  • 文章标题
  • 文章描述

横幅表

  • BannerID (PK)
  • 横幅名称
  • 横幅路径

文章_横幅表

  • ArticleID (FK)(也是与 BannerID 的复合 PK)
  • 横幅 ID (FK)

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

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

结构优势:

  • 可以轻松创建查询语句
  • 可以利用定义的索引
  • ETC..
于 2013-05-07T09:04:01.767 回答
3

除了John Woo 的出色回答之外,我将尝试回答“为什么查询没有返回任何结果”这个问题。

我将撇开该WHERE b.BannerID = 2子句,显然任何样本记录都不符合该子句。

查询的主要问题是IN 子句。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的解决方案将摆脱这两个问题。

于 2013-05-07T13:55:01.227 回答
0

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

我的情况是使用表格来保存一些自定义表单字段元素数据。在不列出整个结构的情况下,我将仅列出显示和重现问题所需的内容。在这种情况下,我还可以确认围绕 IsNumeric 解决了问题。也与子查询结合使用。该示例包含两个项目,模拟自定义字段元素/类型的项目名称和字段值。有些是名字,有些是劳动记录。可能是重量、温度、距离等等,它是客户可定义的额外数据。

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 时转换失败。”</p>

较低的嵌套查询有一个 where 子句,将返回的行限制为仅包含基于数字的数据。然而,更高级别的演员清楚地看到子查询返回中未包含的行。它实际上是查看和处理下层嵌套查询的数据。找不到选择选项标志来防止这种情况。

于 2015-09-09T17:38:44.167 回答