我已经成功解决了按值here交换行的情况 但是通过使用这个功能,我看到拖放行的功能缺乏一些功能 我试图获得解决方案错误的解决方案here并提供解决方案是不够的,因为他们需要额外的列。
这是众所周知的表格:
DROP TABLE IF EXISTS kalksad1;
CREATE TABLE kalksad1(
kalk_id int PRIMARY KEY,
brkalk integer,
brred integer,
description text);
INSERT INTO kalksad1 VALUES
(12, 2, 5, 'text index 12 doc 2 row 5'),
(26, 2, 1, 'text index 26 doc 2 row 1'),
(30, 2, 2, 'text index 30 doc 2 row 2'),
(32, 4, 1, 'text index 32 doc 4 row 1'),
(36, 1, 1, 'text index 36 doc 1 row 1'),
(37, 1, 2, 'text index 37 doc 1 row 2'),
(38, 5, 1, 'text index 38 doc 5 row 1'),
(39, 5, 2, 'text index 39 doc 5 row 2'),
(42, 2, 3, 'text index 42 doc 2 row 3'),
(43, 2, 4, 'text index 43 doc 2 row 4'),
(46, 3, 1, 'text index 46 doc 3 row 1'),
(47, 3, 2, 'text index 47 doc 3 row 2');
操纵对象是在“brkalk”(doc)下重新排序列“brred”(行)的值。
让“brkalk”为2。
现在我想根据拖放需求进行重新排序/交换,只需交换一行看起来不自然。我有数据网格绑定到kalksad1表,所以我将描述在我的数据网格中查找的情况,其中填充了查询“...由orred by Brred”。
如果我可以通过单词来解释查询...
例1:
在doc 2下,我将拖动第4行并将其放到第2行的位置。
因此需要遵循以下步骤:
1)记住第4行的数据
2)在第3行中,将“brred”的值从3更换为4
3)在第2行中,将“brred”的值从2更换为3
4)在步骤1)的记忆数据中,将“brred”的值从4改为2.
例2:
在doc 2下,我将拖动第1行并将其放到第3位。
这可以是这样的:
1)记住第1行的数据
2)在第2行中,将“brred”的值从2更换为1
3)在第3行中,将“brred”的值从3替换为2
4)在步骤1中记住的数据中,将“brred”的值从1更改为3.
这可能是可能的,因为优雅的解决方案包含交换和SO问题like this,this和this。我根据自己的想法做出了例子,但如果存在更好的话,就不应该这样做。
如果有人可以按照与用户Roman Pekar的swapping类似的方式编写描述的查询。
EDIT: Solution based on Example1 from Tometzky
Imports Npgsql
Public Class Form1
Dim dServer As String = "127.0.0.1"
Dim dPort As String = "5432"
Dim dUser As String = "postgres"
Dim dPass As String = yourpass
Dim ddatabase As String = yourdatabase
Private dragrect As Rectangle
Private dragindex, dropindex As Integer
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim conn As New NpgsqlConnection(String.Format( _
"Server={0};Port={1};User Id={2};Password={3};Database={4};", _
dServer, dPort, dUser, dPass, ddatabase))
conn.Open()
Using t As NpgsqlTransaction = conn.BeginTransaction()
Using cmd As New NpgsqlCommand( _
"DROP TABLE IF EXISTS kalksad1;", conn)
cmd.ExecuteNonQuery()
End Using
Using cmd As New NpgsqlCommand( _
"CREATE TABLE kalksad1(" & _
"kalk_id int PRIMARY KEY, " & _
"brkalk integer, " & _
"brred integer, " & _
"description text);", conn)
cmd.ExecuteScalar()
End Using
Using cmd As New NpgsqlCommand( _
"INSERT INTO kalksad1 VALUES" & _
"(12, 2, 5, 'text index 12 doc 2 row 5'), " & _
"(26, 2, 1, 'text index 26 doc 2 row 1'), " & _
"(30, 2, 2, 'text index 30 doc 2 row 2'), " & _
"(32, 4, 1, 'text index 32 doc 4 row 1'), " & _
"(36, 1, 1, 'text index 36 doc 1 row 1'), " & _
"(37, 1, 2, 'text index 37 doc 1 row 2'), " & _
"(38, 5, 1, 'text index 38 doc 5 row 1'), " & _
"(39, 5, 2, 'text index 39 doc 5 row 2'), " & _
"(42, 2, 3, 'text index 42 doc 2 row 3'), " & _
"(43, 2, 4, 'text index 43 doc 2 row 4'), " & _
"(46, 3, 1, 'text index 46 doc 3 row 1'), " & _
"(47, 3, 2, 'text index 47 doc 3 row 2');", conn)
cmd.ExecuteNonQuery()
End Using
t.Commit()
End Using
With DataGridView1
.AllowDrop = True
.MultiSelect = False
.Dock = DockStyle.Fill
.SelectionMode = DataGridViewSelectionMode.FullRowSelect
.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells
.Columns.Add("col1", "ID")
.Columns.Add("col2", "Doc")
.Columns.Add("col3", "Row")
.Columns.Add("col4", "Description")
End With
FillData(0)
End Sub
Private Sub FillData(ByVal dropindex As Integer)
DataGridView1.Rows.Clear()
Try
Using mCon As New NpgsqlConnection(String.Format( _
"Server={0};Port={1};User Id={2};Password={3};Database={4};", _
dServer, dPort, dUser, dPass, ddatabase))
mCon.Open()
Using mCmd = New NpgsqlCommand( _
"SELECT kalk_id, brkalk, brred, description " & _
"FROM kalksad1 " & _
"WHERE brkalk='2' ORDER BY brred", mCon)
Using reader As NpgsqlDataReader = mCmd.ExecuteReader()
While (reader.Read())
DataGridView1.Rows.Add(New String() _
{CStr(reader("kalk_id")), _
CStr(reader("brkalk")), _
CStr(reader("brred")), _
CStr(reader("description"))})
End While
End Using
End Using
End Using
Catch ex As Exception
Debug.Print(ex.Message)
End Try
''selecting a row
If dropindex < 0 Then dropindex = 0
With DataGridView1
.Rows(dropindex).Selected = True
.CurrentCell = .Item(0, dropindex)
End With
End Sub
#Region "dragdrop"
Private Sub DataGridView1_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles DataGridView1.DragDrop
Dim p As Point = Me.PointToClient(New Point(e.X, e.Y))
dropindex = DataGridView1.HitTest(p.X, p.Y).RowIndex
If (e.Effect = DragDropEffects.Move) Then
Dim dragRow As DataGridViewRow = CType(e.Data.GetData(GetType(DataGridViewRow)), DataGridViewRow)
Dim _from As Integer = dragindex + 1 ''grid is zero based, document is 1 based
Dim _to As Integer = dropindex + 1
Dim updown As String = ""
If _from < _to Then ''correction for up
_to = _to + 1
updown = "!"
End If
'' PROCEDURE HERE -----------------------------------------------------------------
Dim affected As Integer = 0
Try
Using conn As New NpgsqlConnection(String.Format( _
"Server={0};Port={1};User Id={2};Password={3};Database={4};", _
dServer, dPort, dUser, dPass, ddatabase))
conn.Open()
Using t As NpgsqlTransaction = conn.BeginTransaction()
Using cmd As New NpgsqlCommand( _
"UPDATE kalksad1 SET brred=_brred " & _
"FROM (" & _
" SELECT " & _
" row_number() OVER (" & _
" ORDER BY brred<" & _to.ToString & " DESC, brred" & updown & "=" & _from.ToString & " DESC, brred>=" & _to.ToString & " DESC, brred" & _
" ) AS _brred," & _
" kalk_id AS _kalk_id " & _
"FROM kalksad1 " & _
"WHERE brkalk=2 " & _
"ORDER BY _kalk_id" & _
") AS _ " & _
"WHERE kalk_id=_kalk_id AND brred!=_brred;", conn)
affected = CInt(cmd.ExecuteNonQuery())
End Using
If affected > 0 Then t.Commit()
End Using
End Using
Catch ex As Exception
Debug.Print(ex.Message)
End Try
''---------------------------------------------------------------------------------
FillData(dropindex) ''clear, fill and select dropped row
End If
End Sub
Private Sub DataGridView1_DragOver(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles DataGridView1.DragOver
e.Effect = DragDropEffects.Move
End Sub
Private Sub DataGridView1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles DataGridView1.MouseDown
dragindex = DataGridView1.HitTest(e.X, e.Y).RowIndex
If dragindex > -1 Then
Dim dragSize As Size = SystemInformation.DragSize
dragrect = New Rectangle(New Point(CInt(e.X - (dragSize.Width / 2)), CInt(e.Y - (dragSize.Height / 2))), dragSize)
Else
dragrect = Rectangle.Empty
End If
End Sub
Private Sub DataGridView1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles DataGridView1.MouseMove
If (e.Button And MouseButtons.Left) = MouseButtons.Left Then
If (dragrect <> Rectangle.Empty AndAlso Not dragrect.Contains(e.X, e.Y)) Then
Me.DoDragDrop(DataGridView1.Rows(dragindex), DragDropEffects.Move)
End If
End If
End Sub
#End Region
End Class
答案 0 :(得分:1)
示例1:
update kalksad1 set brred=_brred
from (
select
row_number() over (
order by brred<2 desc, brred=4 desc, brred>=2 desc, brred
) as _brred,
kalk_id as _kalk_id
from kalksad1
where brkalk=2
order by _kalk_id
) as _
where kalk_id=_kalk_id and brred!=_brred;
示例2:
update kalksad1 set brred=_brred
from (
select
row_number() over (
order by brred<4 desc, brred!=1 desc, brred>=4 desc, brred
) as _brred,
kalk_id as _kalk_id
from kalksad1
where brkalk=2
order by _kalk_id
) as _
where kalk_id=_kalk_id and brred!=_brred;
如果您在(brkalk,brred)
上有唯一索引,则会更复杂,因为在重新编号期间会有重复的brred
。
但是对于很多行,我建议使用在8位计算机上使用BASIC语言时非常有用的东西 - 用差距对行进行编号。
所以而不是:
(26, 2, 1, 'text index 26 doc 2 row 1'),
(30, 2, 2, 'text index 30 doc 2 row 2'),
(42, 2, 3, 'text index 42 doc 2 row 3'),
(43, 2, 4, 'text index 43 doc 2 row 4'),
(12, 2, 5, 'text index 12 doc 2 row 5'),
使用:
(26, 2, 1024, 'text index 26 doc 2 row 1'),
(30, 2, 2048, 'text index 30 doc 2 row 2'),
(42, 2, 3072, 'text index 42 doc 2 row 3'),
(43, 2, 4096, 'text index 43 doc 2 row 4'),
(12, 2, 5120, 'text index 12 doc 2 row 5'),
然后你的例子就像:
update kalksad1 set brred=(2048+1024)/2 where kalk_id=43
,将其更改为:
(26, 2, 1024, 'text index 26 doc 2 row 1'), (43, 2, 1536, 'text index 43 doc 2 row 4'), (30, 2, 2048, 'text index 30 doc 2 row 2'), (42, 2, 3072, 'text index 42 doc 2 row 3'), (12, 2, 5120, 'text index 12 doc 2 row 5'),
update kalksad1 set brred=(4096+3072)/2 where kalk_id=43
,将其更改为:
(30, 2, 2048, 'text index 30 doc 2 row 2'), (42, 2, 3072, 'text index 42 doc 2 row 3'), (26, 2, 3584, 'text index 26 doc 2 row 1'), (43, 2, 4096, 'text index 43 doc 2 row 4'), (12, 2, 5120, 'text index 12 doc 2 row 5'),
只有当目标应该在的行之间没有间隙时,您才需要首先使用例如重新编号行:
update kalksad1 set brred=_brred*1024
from (
select row_number() over (order by brred) as _brred, kalk_id as _kalk_id
from kalksad1
where brkalk=2
order by _brred desc
) as _
where kalk_id=_kalk_id;
这比改变源和目标之间的每一行要多得多。但这只会在可能有很多行要改变时才会发生。
答案 1 :(得分:1)
它通过一组触发器+相关函数来处理Tab键顺序。
brred
的{{1}}会导致旧值和新值之间的所有值旋转,无论是向上还是向下。brkalk
值(对于相同的brred
)递减(向下移动)brkalk
值高于NEW值(对于相同的brred
)递增(向上移动)为了避免永久递归更新,每行需要额外的一些信息:brkalk
(应该只触发触发器,不是应用程序代码。它可以通过视图隐藏应用程序
flipflag
结果:
ALTER TABLE kalksad1 ADD COLUMN flipflag boolean DEFAULT false;
-- This should be an UNIQUE constraint
-- , but that would need to be deferrable.
CREATE INDEX ON kalksad1 (brkalk,brred);
-- Trigger functions for Insert/update/delete
CREATE function rotate_brred()
RETURNS TRIGGER AS $body$
BEGIN
UPDATE kalksad1 fr
SET brred = brred +1
, flipflag = NOT flipflag -- alternating bit protocol ;-)
WHERE NEW.brred < OLD.brred
-- AND OLD.flipflag = NEW.flipflag -- redundant condition
-- AND OLD.brkalk = NEW.brkalk
AND fr.brkalk = NEW.brkalk
AND fr.brred >= NEW.brred
AND fr.brred < OLD.brred
AND fr.kalk_id <> NEW.kalk_id -- exlude the initiating row
;
UPDATE kalksad1 fr
SET brred = brred -1
, flipflag = NOT flipflag
WHERE NEW.brred > OLD.brred
-- AND OLD.flipflag = NEW.flipflag
-- AND OLD.brkalk = NEW.brkalk
AND fr.brkalk = NEW.brkalk
AND fr.brred <= NEW.brred
AND fr.brred > OLD.brred
AND fr.kalk_id <> NEW.kalk_id
;
RETURN NEW;
END;
$body$
language plpgsql;
CREATE function shift_down()
RETURNS TRIGGER AS $body$
BEGIN
UPDATE kalksad1 fr
SET brred = brred -1
, flipflag = NOT flipflag -- alternating bit protocol ;-)
WHERE fr.brred > OLD.brred
AND fr.brkalk = OLD.brkalk
;
RETURN NEW;
END;
$body$
language plpgsql;
CREATE function shift_up()
RETURNS TRIGGER AS $body$
BEGIN
UPDATE kalksad1 fr
SET brred = brred +1
, flipflag = NOT flipflag -- alternating bit protocol ;-)
WHERE fr.brred >= NEW.brred
AND fr.brkalk = NEW.brkalk
;
RETURN NEW;
END;
$body$
language plpgsql;
-- Triggers for Insert/Update/Delete
-- ONLY for the case where brkalk is NOT CHANGED
CREATE TRIGGER shift_brred_u
AFTER UPDATE OF brred ON kalksad1
FOR EACH ROW
WHEN (OLD.flipflag = NEW.flipflag AND OLD.brkalk = NEW.brkalk AND OLD.brred <> NEW.brred)
EXECUTE PROCEDURE rotate_brred()
;
CREATE TRIGGER shift_brred_d
AFTER DELETE ON kalksad1
FOR EACH ROW
EXECUTE PROCEDURE shift_down()
;
CREATE TRIGGER shift_brred_i
BEFORE INSERT ON kalksad1
FOR EACH ROW
EXECUTE PROCEDURE shift_up()
;
-- Test it
UPDATE kalksad1
SET brred = 2
WHERE brkalk = 2
AND brred = 4;
SELECT * FROM kalksad1
ORDER BY brkalk, brred;