我有复杂的功能来交换不同行中同名列的数据。我从第一个读取数据,将其存储在本地临时变量中,从第二行读取数据,如果满足某些条件(最小值/最大值),将其保存到第一行,然后将临时变量保存到第二行,这是一个缓慢且容易出错的操作
所以我认为可能只有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
关于如何使这更短,更快,更优雅,整个故事有问题吗?
答案 0 :(得分:6)
示例:交换doc_num for ids 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)
答案 1 :(得分: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;
向上相同但where id <= %your current id% order by id desc limit 2