簡體   English   中英

將逗號分隔的列表作為數字字符串傳遞,以使用 Npgsql 作為 C# 中 PostgreSQL db 的整數列表進行查詢

[英]Pass comma-separated list as string of numbers to query as list of integers for PostgreSQL db in C# using Npgsql

在我用 C# 編寫的 WinForms 項目中,我正在使用 Npgsql 框架連接到 PostgreSQL 數據庫。

我正在使用 PG 數據庫中的 DataTable 填充幾個 DGV。 我還嘗試實現一個搜索功能,當用戶啟動該功能時,它會在 DGV 的 DataTable 中搜索搜索字符串,然后根據這些結果重建 DGV。 當進程搜索 DataTable 時,它正在構建一個記錄 ID 列表(例如,“1234,2345,3456”),該列表將傳遞給 db 查詢,以僅使用與用戶搜索值匹配的記錄重新填充 DGV。

這是構建 ID 列表和數據庫查詢的搜索 function:

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;
    }
}

然后在我的數據庫 class 中,GetDgvData() function 看起來像這樣:

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() function 中注釋, da.Fill(ds);處的錯誤是 NpgsqlPostgresException,“操作符不存在:integer = text'”

經過一番研究,我在查詢代碼中嘗試了UNNEST() function 並留下了參數代碼。 當我嘗試這樣做時,我得到一個異常說 function 不存在。

經過一番研究,我發現了一個將 Array db 類型轉換為 Integer db 類型的代碼示例,這是GetDgvData() function 參數部分中注釋代碼的第一行。 當我嘗試這樣做時,拋出的異常是“System.InvalidCastException:'無法將類型 System.String 寫入 System.Int32 的數組'”。

經過更多研究,我嘗試了GetDgvData() function 中的第三條注釋行,它用"Array[]"包圍了參數。 我得到的例外是,“操作員不存在:integer = text'”。 我嘗試在查詢中使用和不使用UNNEST() function 。 沒有UNNEST()我得到一個 Npgsql.PostgresException,“操作符不存在:integer = text'”。 使用UNNEST()我得到 PostgresException,“函數 unnest(text) 不存在”。

我還將 ID 構建為List<>並嘗試將其傳遞給GetDgvData()但我沒有正確傳遞它或其他什么,因為當它到達我嘗試的行時,我在GetDgvData()中得到了 null 引用異常對List<>做一些事情 - 包括嘗試查看控制台中的內容。

所以我真的不知道還能從這里嘗試什么。

其他人已經在他們各自的解決方案中成功地使用了上述代碼,所以我只能假設我遺漏了一些東西,但它可能是什么,我不知道。

有很多東西要解構,所以我將嘗試將其歸結為我認為是錯誤的。 我希望這足以讓你走上正軌。

看起來您嘗試使用數組,這絕對是 go 的方式。 問題是您使用了用於離散列表的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 List 作為整數數組或可以通過.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);

代碼的rest是一樣的。

請原諒我對AddWithValue的使用,我知道它在許多圈子中都被不贊成...坦率地說,我喜歡它,並且覺得反對它的 arguments 並沒有超過它的實用性,尤其是 PostgreSQL。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM