Sql 表达式中的列名不明确
Ambiguous Column name in Sql expression
我有两个table;它们通过 Delphi ado link
通过列名称上的关系详细信息连接
1st table 有一堆数据和一个 fileref 作为键 1,
第二个 table 有数据行和一个文件引用作为键 2
1st table 除了一个 fileref 值外还有其他信息,
第二个 table 包含许多文件引用值但不同的帐户
Table 1:
id, fileref, 1, 2, 3, 4, 5, accno, 7, 8, 9, 等等...
table 2:
id, fileref, accno
SELECT * FROM vtindex a
JOIN vi_accno b
ON b.fileref = a.FileRef
WHERE (a.AccNo like '%123456789%') or (b.accno like '%123456789%')
以上是我得到不明确错误的查询
我的想法是,如果我找不到 accno 是 table 1,它必须尝试在 table 2
中找到它
希望这是有道理的,奇怪的是,如果我 运行 MSSMS 中的查询查询 returns 结果没有错误
只需尝试用明确的列名替换 *
并为每个列定义唯一的别名。这将解决歧义错误。下面是一个例子:
更新:
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
内的 FileRef
和 b.Id
列发送到输出,如果我们的假设是正确的,然后您可以将它们从 Select 中删除并简化如下:
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%')
试试这个,
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%')
您在两个表中具有相同的列名,因此使用 * 提取两者并且名称冲突。在 SSMS 中,您可以这样做,因为您只能在屏幕上查看结果,但是一旦您将数据发送到期望列名唯一的目的地,您就会收到错误消息。您必须在 select 列表中明确命名您想要的列。
此外,根据您的问题,我相信您会想要使用 coalesce(b.accno, a.accno)。仅当 B 中的 accno 为 null 时,才会使用 A 中的 accno。
编辑:根据评论,我相信这里是我所指的特定语法。请注意 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%')
您将不得不声明您的列,像这样;
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%'
您的问题是您不止一次使用 id
、fileref
和 accno
等字段(来自每个 table)并且名称冲突。如果您更改只有 3 条记录的 table 上的名称,那么您可以让 table 保持原样。
我有一种预感,你的问题不是由你尝试使用的 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-[=36 中被破坏=] 西雅图,因为它似乎无法识别嵌入在涉及字符串表达式的“LIKE”构造中的参数。一种解决方法可能是在服务器上定义一个存储过程,该过程使用应用程序在 运行 时提供的参数执行 SQL。
因此,我的猜测是因为您使用的是 SQL.Add(),实际上您并没有构建您认为的 Sql。我怀疑你得到的错误实际上是想告诉你 Edit1.Text
是模棱两可的——取决于你的确切实际 Sql,可能 Sql 解析器认为 Edit1.Text是列名。
表 A 和表 B 的 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]
我有两个table;它们通过 Delphi ado link
通过列名称上的关系详细信息连接1st table 有一堆数据和一个 fileref 作为键 1, 第二个 table 有数据行和一个文件引用作为键 2
1st table 除了一个 fileref 值外还有其他信息, 第二个 table 包含许多文件引用值但不同的帐户
Table 1: id, fileref, 1, 2, 3, 4, 5, accno, 7, 8, 9, 等等...
table 2: id, fileref, accno
SELECT * FROM vtindex a
JOIN vi_accno b
ON b.fileref = a.FileRef
WHERE (a.AccNo like '%123456789%') or (b.accno like '%123456789%')
以上是我得到不明确错误的查询
我的想法是,如果我找不到 accno 是 table 1,它必须尝试在 table 2
中找到它希望这是有道理的,奇怪的是,如果我 运行 MSSMS 中的查询查询 returns 结果没有错误
只需尝试用明确的列名替换 *
并为每个列定义唯一的别名。这将解决歧义错误。下面是一个例子:
更新:
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
内的 FileRef
和 b.Id
列发送到输出,如果我们的假设是正确的,然后您可以将它们从 Select 中删除并简化如下:
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%')
试试这个,
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%')
您在两个表中具有相同的列名,因此使用 * 提取两者并且名称冲突。在 SSMS 中,您可以这样做,因为您只能在屏幕上查看结果,但是一旦您将数据发送到期望列名唯一的目的地,您就会收到错误消息。您必须在 select 列表中明确命名您想要的列。
此外,根据您的问题,我相信您会想要使用 coalesce(b.accno, a.accno)。仅当 B 中的 accno 为 null 时,才会使用 A 中的 accno。
编辑:根据评论,我相信这里是我所指的特定语法。请注意 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%')
您将不得不声明您的列,像这样;
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%'
您的问题是您不止一次使用 id
、fileref
和 accno
等字段(来自每个 table)并且名称冲突。如果您更改只有 3 条记录的 table 上的名称,那么您可以让 table 保持原样。
我有一种预感,你的问题不是由你尝试使用的 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-[=36 中被破坏=] 西雅图,因为它似乎无法识别嵌入在涉及字符串表达式的“LIKE”构造中的参数。一种解决方法可能是在服务器上定义一个存储过程,该过程使用应用程序在 运行 时提供的参数执行 SQL。
因此,我的猜测是因为您使用的是 SQL.Add(),实际上您并没有构建您认为的 Sql。我怀疑你得到的错误实际上是想告诉你 Edit1.Text
是模棱两可的——取决于你的确切实际 Sql,可能 Sql 解析器认为 Edit1.Text是列名。
表 A 和表 B 的 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]