3

我有复杂的功能来交换不同行中同名列的数据。我从第一个读取数据,将其存储在本地临时变量中,从第二行读取数据,如果满足某些条件(最小/最大),将其保存到第一行,然后将临时变量保存到第二行,这是缓慢且容易出错的操作.

所以我想也许只用 SQL 就可以达到同样的结果。

这是示例数据:

CREATE TEMP TABLE tbl(
id        int PRIMARY KEY,
doc_num   integer, 
doc_text  text 
);

INSERT INTO tbl VALUES
  (1, 1, 'First column text1'),
  (2, 2, 'First column text2'),
  (4, 3, 'First column text3'),
  (7, 4, 'First column text4');

Piont 仅在所需方向上交换“doc_num”列数据,这可能是我使用单独函数所做的向上或向下。

如果我可以用英语写一个听起来像这样的简单查询:

第一个查询:

SWAP DOC_NUM in row 2 with DOC_NUM in row 3 IF DOC_NUM in row 3 IS <= MAX(DOC_NUM);

第二个查询:

SWAP DOC_NUM in row 3 with DOC_NUM in row 2 IF DOC_NUM in row 2 IS >= MIN(DOC_NUM);

这些查询是否可以用 PostgreSQL 编写以及如何编写?

这是来自“完成工作”并需要改进的真实程序的真实代码(丑陋)。

 Private Sub DataGridView2_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles DataGridView2.KeyDown

    'SWAP --------------------------------------
    If e.Control And e.KeyCode = Keys.Left Then
        Debug.Print("Swap left/down")

        Dim target_nrow As Integer
        Dim target_index As Integer
        Dim selected_nrow As Integer
        Dim selected_index As Integer
        Dim target_row As Integer = selected_row - 1
        Using conn As New NpgsqlConnection(String.Format("Server={0};Port={1};User Id={2};Password={3};Database={4};", dbServer, dbPort, dbUser, dbPass, mydatabase))
            conn.Open()
            Dim t As NpgsqlTransaction = conn.BeginTransaction()

            Using cmd As New NpgsqlCommand( _
                  "SELECT cur_id, doc_num, nrow " & _
                  "FROM " & mytable & " " & _
                  "WHERE doc_num='" & active_doc.ToString & "' AND nrow='" & selected_row.ToString & "'", conn)

                Using dr As NpgsqlDataReader = cmd.ExecuteReader()
                    While dr.Read()
                        selected_index = CInt(dr(0))
                        selected_nrow = CInt(dr(2))
                    End While
                End Using
            End Using

            Using cmd As New NpgsqlCommand( _
                  "SELECT cur_id, doc_num, nrow " & _
                  "FROM " & mytable & " " & _
                  "WHERE doc_num='" & active_doc.ToString & "' AND nrow='" & target_row.ToString & "'", conn)

                Using dr As NpgsqlDataReader = cmd.ExecuteReader()
                    While dr.Read()
                        target_index = CInt(dr(0))
                        target_nrow = CInt(dr(2))
                    End While
                End Using
            End Using

            Dim updated_t As Integer = 0
            Using cmd As New NpgsqlCommand( _
                  "UPDATE " & mytable & " SET " & _
                  "nrow=" & selected_nrow & " " & _
                  "WHERE cur_id=" & target_index.ToString, conn)

                updated_t = CInt(cmd.ExecuteNonQuery())
                cmd.Dispose()
            End Using

            Dim updated_s As Integer = 0
            Using cmd As New NpgsqlCommand( _
                  "UPDATE " & mytable & " SET " & _
                  "nrow=" & target_nrow & " " & _
                  "WHERE cur_id=" & selected_index.ToString, conn)

                updated_s = CInt(cmd.ExecuteNonQuery())
                cmd.Dispose()
            End Using

            If updated_s > 0 And updated_t > 0 Then
                t.Commit()
            Else
                t.Rollback()
            End If

            t.Dispose()
            conn.Close()
            conn.Dispose()
        End Using

        Refreshlist(active_doc)
    End If

    If e.Control And e.KeyCode = Keys.Right Then
        Debug.Print("Swap right/up")

        'similar code to swap up again

    End If

整个故事都是关于如何使这个更短、更快、更优雅的问题?

4

2 回答 2

9

示例:将 doc_num 交换为 id 2 和 4:

UPDATE tbl dst
SET doc_num = src.doc_num
FROM tbl src
WHERE dst.id IN(2,4)
AND src.id IN(2,4)
AND dst.id <> src.id -- don't try this at home!
        ;

SELECT * FROm tbl
ORDER BY id;

结果:

 id | doc_num |      doc_text      
----+---------+--------------------
  1 |       1 | First column text1
  2 |       3 | First column text2
  4 |       2 | First column text3
  7 |       4 | First column text4
(4 rows)
于 2013-08-14T13:11:10.573 回答
2

要交换数据,您可以尝试以下操作:

with cte1 as (
    select
        row_number() over(order by id asc) as row_num,
        id, doc_num
    from tbl
    where id >= %your current id% order by id limit 2
)
update tbl as t set
    doc_num = coalesce(c2.doc_num, t.doc_num)
from cte1 as c1
    left outer join cte1 as c2 on c2.row_num <> c1.row_num
where t.id = c1.id;

参见sql fiddle 演示

向上将是相同的,但where id <= %your current id% order by id desc limit 2

于 2013-08-14T11:57:01.537 回答