【问题标题】:Ambiguous Column name in Sql expressionSql 表达式中的列名不明确
【发布时间】:2016-06-22 12:33:48
【问题描述】:

我有两张桌子;它们通过 Delphi ado 链接通过列名上的关系详细信息加入

第一个表有一堆数据和一个 fileref 作为键 1, 第二个表有数据行和一个 fileref 作为键 2

第一个表有其他信息,但一个 fileref 值, 第二个表包含许多 fileref 值但不同的帐户

表 1: id、fileref、1、2、3、4、5、accno、7、8、9等...

表 2: id、fileref、acno

  SELECT * FROM vtindex a
  JOIN vi_accno b
  ON b.fileref = a.FileRef
  WHERE (a.AccNo like '%123456789%') or (b.accno like '%123456789%')

上面是我得到不明确错误的查询

这个想法是,如果我没有找到 accno 是表 1,它必须尝试在表 2 中找到它

希望这是有道理的,奇怪的是,如果我在 MSSMS 中运行查询,查询会返回没有错误的结果

【问题讨论】:

  • 将其更改为 SELECT a.*, b.*
  • @RichBenner 不工作 m8
  • 我刚刚发布了一个答案,看看它是否解决了它。
  • 绝对确定您的 AdoQuery 的 Sql.Text 属性包含您在 q 中引用的确切 SQL 语句吗?我问的原因是,通过使用 Sql.Add() 构造它很容易引入 MS SSMS 捕获的语法错误。
  • 好的,它通过将表 2 的名称重命名为 viid、vifileref、viaccno 然后使用这里每个人的所有建议来解决这个问题,所以感谢大家迄今为止提供的所有出色答案和帮助@RichBenner @ MartynA @ JoeC @ VahidFarahmandian

标签: sql sql-server delphi ado


【解决方案1】:

你将不得不声明你的列,像这样;

SELECT
     a.ID A_ID
    ,a.fileref A_fileref
    ,a.Field1 A_Field1
    ,a.Field2 A_Field2
    ,a.accno A_Accno
    ,b.id B_ID
    ,b.fileref B_fileref
    ,b.accno B_accno
FROM vtindex a
JOIN vi_accno b
    ON a.fileref = b.fileref
WHERE a.AccNo like '%123456789%'
    OR b.accno like '%123456789%'

如果你只想声明一些,那么就这样做;

SELECT
     a.*
    ,b.id B_ID
    ,b.fileref B_fileref
    ,b.accno B_accno
FROM vtindex a
JOIN vi_accno b
    ON a.fileref = b.fileref
WHERE a.AccNo like '%123456789%'
    OR b.accno like '%123456789%'

您的问题是您多次(来自每个表)使用 idfilerefaccno 等字段,并且名称发生冲突。如果您更改只有 3 条记录的表上的名称,那么您可以保持原样。

【讨论】:

  • 这解决了我的问题,我重命名了第二张表上的字段并使用此查询运行:SELECT a.*, b.* FROM vtindex a JOIN vi_accnot b ON b.vifileref = a.FileRef WHERE (a.AccNo like ''%'+edit1.text+'%'') 或 (b.viaccno like ''%'+edit1.Text+'%'')
  • 所以事实上每个人都帮助了我得到答案的方向,所以谢谢大家
  • 只是为了清楚他对“如果您更改只有 3 条记录的表上的名称,那么您可以让表保持原样”。是我点击的答案...
【解决方案2】:

您在两个表中具有相同的列名,因此使用 * 会拉取两者并且名称冲突。在 SSMS 中,您可以这样做,因为您只是在屏幕上查看结果,但是一旦您将数据发送到期望列名唯一的目的地,您就会收到错误消息。您必须在选择列表中明确命名所需的列。

另外,根据您的问题,我相信您会想要使用 coalesce(b.accno, a.accno)。仅当 B 中的 accno 为空时,才会使用 A 中的 accno。

编辑:基于 cmets,我相信这是我所指的特定语法。请注意,coalesce 是如何通过只有一个具有您想要的名称的字段来解决列别名问题的。

SELECT ID, FileRef, cnum, Month, Type, Typei, 
       PropDesc, Coalesce(a.accno, b.accno) AccNo, person, client, idno,        
       Consultant, Memo, qclose, vtdate 
    FROM vtindex a
    Join vi_accno b ON b.fileref = a.FileRef
    WHERE (a.AccNo like '%123456789%') or (b.accno like '%123456789%')

【讨论】:

  • 不明确的列名id,然后我从sql中删除id然后它抱怨不明确的列名fileref?大声笑
【解决方案3】:

只需尝试用明确的列名替换* 并为每个列定义唯一的别名。这将解决歧义错误。下面是一个例子:

更新:

SELECT a.*,b.Id as b_Id, b.Fileref as b_Fileref, b.accno as b_accno FROM vtindex a
JOIN vi_accno b
ON b.fileref = a.FileRef
WHERE (a.AccNo like '%123456789%') or (b.accno like '%123456789%')

正如@Joe C 所评论的那样,我们认为您不需要将vi_accno 中的FileRefb.Id 列发送到输出,如果我们的假设是正确的,那么您可以将它们从您的选择中删除并简化如下:

SELECT a.*, b.accno as b_accno FROM vtindex a
JOIN vi_accno b
ON b.fileref = a.FileRef
WHERE (a.AccNo like '%123456789%') or (b.accno like '%123456789%')

【讨论】:

  • 哎呀,列引用太多了,难道没有更简单的方法
  • 有一些免费工具可以让这变得更容易。对于 SSMS,我有时会使用 Apex Sql Complete 来自动创建选择列表。
  • @Troz 您可以使用第三方工具,例如“Apex”或“RedGate SQL Toolbelt”。或者您可以选择您的查询,然后右键单击它并选择“在编辑器中设计查询”。这些选项将帮助您管理列及其别名。
  • 我以为我很聪明,并尝试了 'SELECT a.*, b.fileref as bfileref, b.accno as baccno FROM vtindex a JOIN vi_accno b ON b.fileref = a.FileRef WHERE (a .AccNo like '%123456789%') 或 (b.accno like '%123456789%')' 但没有运气再模棱两可了
  • 如果我将 accno a 移动到 accno b 会更容易,然后只使用 fileref 上的连接查询 b,问题是,有数据文件链接到列名,所以如果我开始通过别名更改列名然后程序不会将数据读取到字段@JoeC
【解决方案4】:

试试这个,

SELECT a.*,b.* FROM vtindex a
    JOIN vi_accno b
    ON b.fileref = a.FileRef
    WHERE (a.AccNo like '%123456789%') or (b.accno like '%123456789%')

【讨论】:

  • 这将给出与 accno 在 a 和 b 中都存在相同的错误。
  • 这不起作用“列前缀'a'与查询中使用的表名或别名不匹配”
  • 请检查where子句中的列名,我认为列名不匹配。
  • 对不起,我忘记了我恢复到以前的查询,它给出了同样模棱两可的结果
  • 确切代码:form3.adoquery1.SQL.Add('SELECT a.*,b.* FROM vtindex a'); form3.adoquery1.SQL.Add('JOIN vi_accno b'); form3.adoquery1.SQL.Add('ON a.fileref = b.FileRef'); form3.adoquery1.SQL.Add('WHERE (a.AccNo like ''%'+edit1.text+'%'') or (b.accno like ''%'+edit1.Text+'%'')');
【解决方案5】:

我有预感,您的问题与其说是您尝试使用的 Sql,不如说是您尝试使用完全不必要且容易出错的 SQL.Add() 构建它的方式。

以下代码在 D7 中针对 Sql Server 2014 数据库中的 2 个表正确执行,并且没有任何类型的投诉或错误。

procedure TForm1.FormCreate(Sender: TObject);
var
  S : String;
begin
  // WARNING: Do not use this Sql in a live application
  // There is a risk of Sql-Injection because  the Sql includes the
  // contents of Edit1.Text.  Use a parameterised query instead!
  S := 'select a.*, b.*'#13#10;
  S := S + 'from TableA a join TableB b'#13#10;  // the #13#10 can be replaced by a single space, 
  //  if you prefer
  S := S + 'on a.fileref = b.fileref'#13#10;
  S := S + 'where (a.accno like ''%' + Edit1.Text + '%'')'#13#10;
  S := S + 'or (b.accno like ''%' + Edit1.Text + '%'')'#13#10;

  AdoQuery1.SQL.Text := S;
  AdoQuery1.Open;
end;

请注意始终使用单引号,不要使用双引号。

重要提示 直接从 TEdit 控件的内容构造 Sql 会使您的应用对 Sql Injection (https://en.wikipedia.org/wiki/SQL_injection) 负责。您应该改用参数化 Sql。然而, 话虽如此,AdoDB.Pas 中解析 Sql 以创建参数的例程,TAdoCommand.ParseSql 似乎在 D7-Delphi Seattle 中被破坏,因为它似乎无法识别嵌入在 ` 中的参数LIKE' 涉及字符串表达式的构造。一种解决方法可能是在服务器上定义一个存储过程,该过程使用应用程序在运行时提供的参数执行 SQL。

所以,我的猜测是因为您使用的是 SQL.Add(),实际上您并没有构建您认为的 Sql。我怀疑你得到的错误实际上是想告诉你Edit1.Text 是模棱两可的——取决于你的实际Sql,可能Sql 解析器认为Edit1.Text 是列的名称。

TableA 和 TableB 的 Sql DDL:

CREATE TABLE [dbo].[TableA](
    [ID] [int] NOT NULL,
    [FileRef] [int] NULL,
    [AccNo] [varchar](32) NULL,
PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

CREATE TABLE [dbo].[TableB](
    [ID] [int] NOT NULL,
    [FileRef] [int] NULL,
    [AccNo] [varchar](32) NULL,
PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

【讨论】:

  • SQL.Add 没有问题,OP 说他这样做在哪里?另一方面,如果你不需要的话,永远不要构造 SQL。您的代码示例容易出现 SQL 注入,请对其进行调整(人们会阅读您的答案并不假思索地复制/粘贴)。总是(我的意思是总是)使用参数...
  • @whosrdaddy:我会尽快调整答案。同时,在哪里?在 OP 对 DineshDB 答案的评论中“确切代码:form3.adoquery1.SQL.Add('SELECT a.*,b.* FROM vtindex a'); form3.adoquery1.SQL.Add('JOIN vi_accno b '); form3.adoquery1.SQL.Add('ON a.fileref = b.FileRef'); form3.adoquery1.SQL.Add('WHERE (a.AccNo like ''%'+edit1.text+'%'' ) 或 (b.accno like ''%'+edit1.Text+'%'')'); "
  • @whosrdaddy:顺便说一句,我同意 SQL.Add()原则上没有任何问题,它似乎在 SO qs 中异常频繁地处理错误的 SQL .
  • 啊错过了,应该在问题中;)
  • 如果表 1 中的列名相同,则不是 edit1.text -> 表 2 我的印象是它们必须是相同的名称才能在 2 个表之间建立关系在delphi ide中,但是当您在IDE中将2个表链接在一起时,您不需要名称可以完全不同
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-30
  • 2012-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-13
相关资源
最近更新 更多