在用C#编写的WinForms项目中,我正在使用Npgsql框架连接到PostgreSQL数据库。
我正在用PG数据库中的DataTable填充几个DGV。我还尝试实现一种搜索功能,当用户启动该搜索功能时,将在DGV的DataTable中搜索搜索字符串,然后根据这些结果重建DGV。在该过程搜索DataTable时,它正在构建记录ID的列表(例如,“ 1234,2345,3456”),该列表将传递给db查询,以便仅使用与用户搜索值匹配的记录来重新填充DGV。
这是我的搜索功能,用于构建ID和数据库查询的列表:
private void btnSearch_Click(object sender, EventArgs e)
{
string searchText = txtSearchCatsGrid.Text;
string idToAddToList = "";
string tempIdList = "";
string idListForQuery = "";
DataTable dt = ((DataTable)dgvCategories.DataSource);
string cellValue;
int rowIndex;
int columnIndex;
for (rowIndex = 0; rowIndex <= dt.Rows.Count - 1; rowIndex++)
{
for (columnIndex = 0; columnIndex <= dt.Columns.Count - 1; columnIndex++)
{
cellValue = dt.Rows[rowIndex][columnIndex].ToString();
if (searchText == cellValue)
{
// Set corresponding dgvCategories cell's background color to yellow
dgvCategories[columnIndex, rowIndex].Style.BackColor = Color.Yellow;
idToAddToList = dt.Rows[rowIndex][0].ToString();
}
}
if (!string.IsNullOrEmpty(idToAddToList) && !tempIdList.Contains(idToAddToList)) tempIdList += idToAddToList + ",";
}
if (!string.IsNullOrEmpty(tempIdList))
{
// Remove the trailing comma that was added the last time an id was added to the list
idListForQuery = tempIdList.Remove(tempIdList.Length - 1, 1);
}
Console.WriteLine("idListForQuery: " + idListForQuery);
// Refresh dgvCategories with just rows from search, using idListForQuery
string companyFilter = cboSelectCompany.Text;
string categoryFilter = cboSelectCategory.Text;
db categoriesData = new db();
if (categoryFilter == "All Categories")
{
string catsQuery = "SELECT id, category, source_company, old_value, old_desc, new_value, new_desc, reference1, reference2 " +
"FROM masterfiles.xref"+
" WHERE company_name = @company" +
" AND id IN (@idList)" +
//" AND id IN (SELECT UNNEST(@idList))" +
" ORDER BY category, old_value, source_company";
this.dtCategories = categoriesData.GetDgvData(catsQuery, companyFilter, categoryFilter, idListForQuery);
}
else
{
string catsQuery = "SELECT id, category, source_company, old_value, old_desc, new_value, new_desc, reference1, reference2 " +
"FROM masterfiles.xref" +
" WHERE company_name = @company" +
" AND category = @category" +
" AND id IN (@idList)" +
//" AND id IN (SELECT UNNEST(@idList))" +
" ORDER BY old_value, source_company";
this.dtCategories = categoriesData.GetDgvData(catsQuery, companyFilter, categoryFilter, idListForQuery);
}
dgvCategories.DataSource = this.dtCategories;
if (dtCategories.Rows.Count == 0)
{
return;
}
else
{
dgvCategories.Columns[0].Visible = false;
dgvCategories.Rows[0].Cells[0].Selected = false;
}
}
然后在我的数据库类中,GetDgvData()函数如下所示:
public DataTable GetDgvData(string selectQuery, string companyFilter, string categoryFilter, string idFilter)
{
using (NpgsqlConnection conn = new NpgsqlConnection(connString))
using (NpgsqlCommand cmd = new NpgsqlCommand(selectQuery, conn))
{
cmd.Parameters.Add(new NpgsqlParameter("company", companyFilter));
if (!string.IsNullOrEmpty(idFilter)) cmd.Parameters.Add(new NpgsqlParameter("idList", idFilter);
//if (!string.IsNullOrEmpty(idFilter)) cmd.Parameters.Add("idList", NpgsqlDbType.Array | NpgsqlDbType.Integer).Value = idFilter;
//if (!string.IsNullOrEmpty(idFilter)) cmd.Parameters.Add(new NpgsqlParameter("idList", "ARRAY[" + idFilter + "]"));
if (categoryFilter != "All Categories") cmd.Parameters.Add(new NpgsqlParameter("category", categoryFilter));
conn.Open();
DataSet ds = new DataSet();
using (NpgsqlDataAdapter da = new NpgsqlDataAdapter(cmd))
{
da.Fill(ds);
}
return ds.Tables[0];
}
}
我在使用该ID列表作为查询中的参数时遇到麻烦。
您会注意到,在两个代码块中我都注释了几行。这些都是传递列表的各种尝试,所有这些尝试都会在行da.Fill(ds);
[当我只在查询构建代码中使用未注释的行AND id IN (@idList)
和添加参数if (!string.IsNullOrEmpty(idFilter)) cmd.Parameters.Add(new NpgsqlParameter("idList", idFilter);
的“常规”方法时,也在GetDgvData()
函数中进行了注释,da.Fill(ds);
处的错误是NpgsqlPostgresException,“运算符不存在:integer = text'”。
经过一些研究,我尝试在查询代码中使用UNNEST()
函数,并保留参数代码。当我尝试该操作时,出现异常提示该函数不存在。
[经过更多研究之后,我发现了一个将Array db类型转换为Integer db Type的代码示例,这是GetDgvData()
函数的parameter部分中的注释代码的第一行。当我尝试这样做时,抛出的异常是“ System.InvalidCastException:'无法将System.String类型写为System.Int32的数组'”]
[经过更多研究之后,我尝试了GetDgvData()
函数中的第三条注释行,该行用"Array[]"
包围了该参数。我得到的异常是,“运算符不存在:integer = text'”。我尝试了在查询中是否包含UNNEST()
函数。没有UNNEST()
,我得到一个Npgsql.PostgresException,“运算符不存在:integer = text'”。使用UNNEST()
我得到了PostgresException,“函数unnest(text)不存在'”。
我也将ids构造为List<>
,并尝试将其传递给GetDgvData()
,但是我没有正确地传递它或其他东西,因为当GetDgvData()
到达行我尝试使用List<>
进行操作-包括仅尝试查看控制台中的内容。
所以我真的不知道从这里还能尝试什么。
其他人已经在各自的解决方案中成功使用了上面的代码,所以我只能假设我缺少什么,但是可能是什么,我不知道。
有很多需要解构的东西,所以我将尝试将其归结为我认为是错误的。我希望这足以使您步入正轨。
似乎您试图使用数组,这绝对是正确的方法。问题是您使用了IN
,它用于离散列表。对于阵列,您需要使用ANY
。
select *
from foo
where bar in (1, 2, 3)
翻译为
select *
from foo
where bar = any (array[1, 2, 3]) -- or shorthand any ('{1,2,3}')
第二,除非使用动态SQL,否则如果您调用绑定变量,则需要为其分配一个值。反之则不正确。因此,一旦在SQL中声明@category
,则必须在C#中为其分配一个值。解决此问题的一种可能方法是将您的C#逻辑包装在SQL中-我不认为您会因此受到性能的影响。
您修改后的查询将如下所示:
SELECT
id, category, source_company, old_value, old_desc, new_value, new_desc,
reference1, reference2
FROM masterfiles.xref
WHERE
company_name = @company
AND (category = @category or @category = 'All Categories')
AND id = any (@idList)
ORDER BY old_value, source_company
然后,我将进行的关键更改是将您的ID列表传递为整数数组或可以通过.ToArray()转换为整数数组的东西。
public DataTable GetDgvData(string selectQuery, string companyFilter,
string categoryFilter, int[] idFilter)
{
using (NpgsqlConnection conn = new NpgsqlConnection(connString))
{
using (NpgsqlCommand cmd = new NpgsqlCommand(selectQuery, conn))
{
cmd.Parameters.AddWithValue("company", NpgsqlTypes.NpgsqlDbType.Varchar,
companyFilter);
cmd.Parameters.AddWithValue("category", NpgsqlTypes.NpgsqlDbType.Varchar,
categoryFilter);
cmd.Parameters.AddWithValue("idList", NpgsqlTypes.NpgsqlDbType.Array |
NpgsqlTypes.NpgsqlDbType.Integer, idFilter);
其余代码相同。
原谅我对AddWithValue
的使用在很多圈子里都皱着眉头。坦率地说,我喜欢它,并且感到反对它的论点并没有超过它的用处,特别是在PostgreSQL中。