这里进行切换的情况是:我有一个页面,我可以一次编辑多个记录(假设发票)。它们没有在下面显示,但它们被编辑为一个记录。在页面加载时,只有当它们在整个记录集中相同时,值才会显示在给定字段中。查找其对记录位置可以安全地在组合组记录
现在,最重要的是,我也想编辑子记录(1-n的关系;发票的线)。我设法证明发票行记录是相同throughought所有编辑的发票,像这样:
Invoice 1 Invoice 2 Lines edited
A B D
D C G
F D =>
G E
G
假设A,B,......是发票行
订购事宜,透过线的发票,因此发票的每一行都有一个位置字段。这是我想要做的:允许重新排列发票的行。编辑一张发票时,这是一件容易的事。但是,当一次编辑多个发票时,会出现一些问题。考虑以下几点:
Invoice 1 Invoice 2 Lines edited
F(1) B(1) F(1)
A(2) C(2) B(2)
B(3) D(3) => C(3)
C(4) F(4)
E(5)
当线B后移动线F,在发票1,F不仅B之后,还A之后,并且用户不知道它移动;在发票2中,F已经在B之后,但用户不知道它。所以B应该放在F(在位置3)之前还是留在原来的位置?这不清楚。
我想要做的是防止重新排序时的行为是不明确(或意外),并允许它在其他情况下。这是我的解决方案:对于发票的每一行,找到它是否可以向上移动一个步骤(position--),以及是否可以向下移动一个步骤(位置++)。怎么样 ?对于在编辑页面中的每个邻居对线(在本例中:F-B; B-C)中,如果在源发票线对应的对是邻居并以相同的顺序中,一对可以切换。因此,在本例中,这意味着B和C可以切换,但不F和B.因此结果将是:
Lines edited move down move up
F no no
B yes no
C no yes
这里是或多或少我目前的状况:
CREATE TABLE [InvoiceLine](
[id] [int] IDENTITY(1,1) NOT NULL,
[invoiceId] [int] NOT NULL,
[position] [int] NOT NULL,
[text] [nvarchar](255) NULL,
[price] [decimal](18,2) NULL,
CONSTRAINT [PK_InvoiceLine] PRIMARY KEY CLUSTERED ([id] ASC) ON [PRIMARY]
)
CREATE TABLE [Invoice](
[id] [int] IDENTITY(1,1) NOT NULL,
[customerId] [int] NULL,
CONSTRAINT [PK_Invoice] PRIMARY KEY CLUSTERED ([id] ASC) ON [PRIMARY]
)
INSERT INTO [Invoice]([customerId]) VALUES
(1000),
(2000);
INSERT INTO [InvoiceLine]([invoiceId],[position],[text],[price]) VALUES
(1000,1,'F',10.5),
(1000,2,'A',3.0),
(1000,3,'B',4.0),
(1000,4,'C',1.0),
(1000,5,'E',1.0),
(2000,1,'B',4.15),
(2000,2,'C',1.35),
(2000,3,'D',1.20),
(2000,4,'F',12.10);
DECLARE @ids TABLE(n int);
INSERT INTO @ids (n) VALUES (1000),(2000);
DECLARE @n int;
SET @n = (SELECT COUNT(*) FROM @ids);
SELECT
CAST(text AS nvarchar) AS id,
CASE WHEN rank_position = _rankMain THEN position ELSE NULL END AS position,
CASE WHEN rank_text = _rankMain THEN text ELSE NULL END AS text,
CASE WHEN rank_price = _rankMain THEN price ELSE NULL END AS price,
0 AS isRecordDeleted
FROM (
SELECT
T4.position, RANK() OVER (PARTITION BY T4.text, T4.position ORDER BY id) AS rank_position,
T4.text, RANK() OVER (PARTITION BY T4.text ORDER BY id) AS rank_text,
T4.price, RANK() OVER (PARTITION BY T4.text, T4.price ORDER BY id) AS rank_price,
RANK() OVER (PARTITION BY T4.text ORDER BY id) AS _rankMain,
_cnt
FROM
(
-- Filter lines
SELECT
text,
(
SELECT COUNT(id) FROM InvoiceLine WHERE invoiceId IN (SELECT * FROM @ids) AND text = T2.text
) AS _cnt FROM
(
-- add rank on text field (an invoice line is considered equal to another one if both text fields are equal)
SELECT RANK() OVER (PARTITION BY text ORDER BY invoiceId) AS rnk, text FROM
(
-- distinct lines
SELECT DISTINCT invoiceId, text FROM InvoiceLine WHERE invoiceId IN (SELECT n FROM @ids)
) T1
) T2
WHERE rnk = (SELECT COUNT(n) FROM @ids)
) T3 INNER JOIN InvoiceLine T4 ON T4.text = T3.text
) T5 WHERE _cnt = _rankMain ORDER BY position
我问题是:
我应该如何变换查询(实际存储过程)获得“向上”和“向下移动”领域?
我得到的第一个想法是从结果中获取所有相邻对,并在每个源发票行中找到它们的距离(距离是位置差的绝对值),并取距离的最大值。如果最大值等于1,并且差异全部具有相同的符号,则可以切换该对的位置。但我不知道怎么翻译,为SQL ...
[编辑]一两件事:顶编辑的记录应始终都动起来=没有,而最低的编辑的记录应该始终有下移=无。
[EDIT 2012-02-23]添加了ORDER BY在查询
[EDIT 2012-02-23] 这里的端部的第二组数据的其预期的输出:
INSERT INTO [Invoice]([customerId]) VALUES
(1000),
(2000),
(3000);
INSERT INTO [InvoiceLine]([invoiceId],[position],[text],[price]) VALUES
(1000,1,'F',10.5),
(1000,2,'A',3.0),
(1000,3,'B',4.0),
(1000,4,'C',1.0),
(1000,5,'E',1.0),
(1000,6,'G',4.2),
(1000,7,'H',9.0),
(1000,8,'K',9.0),
(2000,1,'B',4.15),
(2000,2,'C',1.35),
(2000,3,'D',1.20),
(2000,4,'F',12.10),
(2000,6,'G',4.2),
(2000,7,'H',2.7),
(2000,8,'I',1.3),
(3000,1,'B',41.15),
(3000,2,'C',15.35),
(3000,3,'D',12.20),
(3000,4,'F',11.10),
(3000,5,'I',4.0),
(3000,6,'G',4.2),
(3000,7,'H',6.7),
(3000,8,'E',7.3);
DECLARE @ids TABLE(n int);
INSERT INTO @ids (n) VALUES (1000),(2000),(3000);
应该产生:
id position text price isRecordDeleted moveUp moveDown
B NULL B NULL 0 no yes
C NULL C NULL 0 yes no
F NULL F NULL 0 no no
G 6 G 4.20 0 no yes
H 7 H NULL 0 yes no
[编辑2012-02-24] 和重复行应只出现一次,并具有为moveUp和下移,只有当他们是直接邻居
这里是第三组数据的其预期输出:
INSERT INTO [Invoice]([customerId]) VALUES
(1000),
(2000),
(3000);
INSERT INTO [InvoiceLine]([invoiceId],[position],[text],[price]) VALUES
(1000,1,'F',10.5),
(1000,2,'A',3.0),
(1000,3,'B',4.0),
(1000,4,'C',1.0),
(1000,5,'E',1.0),
(1000,6,'J',3.2),
(1000,7,'G',4.2),
(1000,8,'H',9.0),
(1000,9,'K',9.0),
(1000,10,'F',3.0),
(2000,1,'B',4.15),
(2000,2,'C',1.35),
(2000,3,'D',1.20),
(2000,4,'C',1.35),
(2000,5,'F',12.10),
(2000,6,'J',6.2),
(2000,7,'G',4.2),
(2000,8,'H',2.7),
(2000,9,'H',3.1),
(2000,10,'I',1.3),
(3000,1,'B',41.15),
(3000,2,'C',15.35),
(3000,3,'D',12.20),
(3000,4,'F',11.10),
(3000,5,'I',4.0),
(3000,6,'J',2.3),
(3000,7,'G',4.2),
(3000,8,'H',6.7),
(3000,9,'E',7.3);
DECLARE @ids TABLE(n int);
INSERT INTO @ids (n) VALUES (1000),(2000),(3000);
应该产生:
id position text price isRecordDeleted moveUp moveDown
B NULL B NULL 0 no no
C NULL C NULL 0 no no
F NULL F NULL 0 no no
J 6 J NULL 0 no yes
G 7 G 4.20 0 yes no
H NULL H NULL 0 no no
或者更好:
id position text price isRecordDeleted moveUp moveDown
B NULL B NULL 0 no no
C NULL C NULL 0 no no
F NULL F NULL 0 no no
J 6 J NULL 0 no yes
G 7 G 4.20 0 yes yes
H NULL H NULL 0 yes no
[EDIT 2012-03-02]
第二个结果是更好的,因为虽然中的H发票2000中出现两次,这两条线是邻居,因此它是安全的切换的h和g的位置:均为H线将被切换。
但是这最后的结果可能会导致过于复杂的查询。
+1将表结构和数据作为脚本提供给我们。当您尝试回答问题时,您会惊讶于节省多少时间。 – 2012-02-17 13:22:06
难以帮助,因为我无法想象用户正在使用您的界面。这是什么类型的应用程序,目标是什么?为什么用户一次编辑多个发票?他们是否在发票下看到所有这些行以及“编辑过的行”?当值在给定的“字段”中显示时......您指的是什么“字段”? “编辑过的行”是指所有发票中常见的行吗?这个列表是否因使用界面而改变?为什么用户需要知道编辑过的行?当用户“移动一条线”时,他们是否在发票内水平拖放? – sisdog 2012-02-18 07:06:17
@sisdog,答案1:其实,我正在创建类似于应用程序生成器的东西(更像是脚手架,现在只是部分)。所以这应该适用于1-n关系的一般情况。发票中可以包含n行发票的发票就是这种情况。 – Daniel 2012-02-20 08:20:55