如何从SQL Server中获取大于多列值的值,而不是使用复合键字段



如果我有包含以下数据的表:

分支,类型,数字=>这将生成复合密钥字符串"keyfield">

分支的长度,类型为[int][4]
数字的长度为[int][7]

数据如下:

branch, type, number
13,     1309,   1    row1
13,     1309,   2    row2
13,     1310,   1    row3
14,     1309,   1    row4

所以我有一个名为"keyfield"列的keyfield->,但我不需要使用它,它可以工作,但我只需要使用表达式而不是字符串keyfield例如:
如果我需要获得大于上面第2行的行:我写道:

SELECT TOP 1 * FROM TABLE WHERE KeyField > '0013130900002'

-->我不喜欢用字符串作为复合键。。

我也不能手动这样做:

SELECT TOP 1 * FROM  TABLE WHERE brn > 1 AND type > 1309 and num > 2

它不会起作用。。。所以我只需要通过表达式得到下一行

例如:getGreatRow(11309,2);//这将返回第3行我需要做的事情。以便此功能可以直接与C#和屏幕上的文本框一起使用!!我需要选择唯一一个比当前记录或我指定的表达式的值大的前1个记录。

编辑

我使用GordonSQL生成了一个C#,其中包含我想要的主键列表。感谢戈登。

在C#中使SQL查询自动生成:

public List<EntryTable> Tables { get; private set; }
public List<BufferElement> Buffer { get; private set; }
string Query = string.Empty;
for (int i = 0; i < Tables[0].PrimaryKeys.Count; i++)
{
Query += "(";
for (int j = 0; j < i; j++)
{
switch (Tables[0].PrimaryKeys[j].CLRType)
{
case CLRType.CLR_BYTE:
Query += $"{Tables[0].PrimaryKeys[j].KeyPart} = {Convert.ToByte(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[j].KeyPart).Value)} AND ";
break;
case CLRType.CLR_INT16:
Query += $"{Tables[0].PrimaryKeys[j].KeyPart} = {Convert.ToInt16(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[j].KeyPart).Value)} AND ";
break;
case CLRType.CLR_INT32:
Query += $"{Tables[0].PrimaryKeys[j].KeyPart} = {Convert.ToInt32(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[j].KeyPart).Value)} AND ";
break;
case CLRType.CLR_INT64:
Query += $"{Tables[0].PrimaryKeys[j].KeyPart} = {Convert.ToInt64(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[j].KeyPart).Value)} AND ";
break;
case CLRType.CLR_SINGLE:
Query += $"{Tables[0].PrimaryKeys[j].KeyPart} = {Convert.ToSingle(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[j].KeyPart).Value)} AND ";
break;
case CLRType.CLR_DOUBLE:
Query += $"{Tables[0].PrimaryKeys[j].KeyPart} = {Convert.ToDouble(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[j].KeyPart).Value)} AND ";
break;
case CLRType.CLR_DECIMAL:
Query += $"{Tables[0].PrimaryKeys[j].KeyPart} = {Convert.ToDecimal(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[j].KeyPart).Value)} AND ";
break;
case CLRType.CLR_Boolean:
Query += $"{Tables[0].PrimaryKeys[j].KeyPart} = {Convert.ToBoolean(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[j].KeyPart).Value)} AND ";
break;
case CLRType.CLR_STRING:
Query += $"{Tables[0].PrimaryKeys[j].KeyPart} = '{Convert.ToString(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[j].KeyPart).Value)}' AND ";
break;
case CLRType.CLR_DATETIME:
Query += $"{Tables[0].PrimaryKeys[j].KeyPart} = '{Convert.ToDateTime(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[j].KeyPart).Value)}' AND ";
break;
case CLRType.CLR_TIME:
Query += $"{Tables[0].PrimaryKeys[j].KeyPart} = '{TimeSpan.Parse(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[j].KeyPart).Value)}' AND ";
break;
}
}
switch (Tables[0].PrimaryKeys[i].CLRType)
{
case CLRType.CLR_BYTE:
Query += $"{Tables[0].PrimaryKeys[i].KeyPart} > {Convert.ToByte(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[i].KeyPart).Value)}";
break;
case CLRType.CLR_INT16:
Query += $"{Tables[0].PrimaryKeys[i].KeyPart} > {Convert.ToInt16(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[i].KeyPart).Value)}";
break;
case CLRType.CLR_INT32:
Query += $"{Tables[0].PrimaryKeys[i].KeyPart} > {Convert.ToInt32(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[i].KeyPart).Value)}";
break;
case CLRType.CLR_INT64:
Query += $"{Tables[0].PrimaryKeys[i].KeyPart} > {Convert.ToInt64(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[i].KeyPart).Value)}";
break;
case CLRType.CLR_SINGLE:
Query += $"{Tables[0].PrimaryKeys[i].KeyPart} > {Convert.ToSingle(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[i].KeyPart).Value)}";
break;
case CLRType.CLR_DOUBLE:
Query += $"{Tables[0].PrimaryKeys[i].KeyPart} > {Convert.ToDouble(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[i].KeyPart).Value)}";
break;
case CLRType.CLR_DECIMAL:
Query += $"{Tables[0].PrimaryKeys[i].KeyPart} > {Convert.ToDecimal(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[i].KeyPart).Value)}";
break;
case CLRType.CLR_Boolean:
Query += $"{Tables[0].PrimaryKeys[i].KeyPart} > {Convert.ToBoolean(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[i].KeyPart).Value)}";
break;
case CLRType.CLR_STRING:
Query += $"{Tables[0].PrimaryKeys[i].KeyPart} > '{Convert.ToString(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[i].KeyPart).Value)}'";
break;
case CLRType.CLR_DATETIME:
Query += $"{Tables[0].PrimaryKeys[i].KeyPart} > '{Convert.ToDateTime(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[i].KeyPart).Value)}'";
break;
case CLRType.CLR_TIME:
Query += $"{Tables[0].PrimaryKeys[i].KeyPart} > '{TimeSpan.Parse(Buffer.Find(x => x.ID == Tables[0].PrimaryKeys[i].KeyPart).Value)}'";
break;
}
Query += $") {(Tables[0].PrimaryKeys.Count > 1 && i != Tables[0].PrimaryKeys.Count - 1 ? " OR " : string.Empty)} n";
}
Query += $"ORDER BY {string.Join(" ASC, ", Tables[0].PrimaryKeys.Select(x => x.KeyPart).ToArray())} ASC";
SelectCommand = $"SELECT TOP 1 * FROM {Tables[0].Table} WHERE " + Query;

如果您试图获取1, 1309, 2之后的下一行

SELECT TOP 1 *
FROM TABLE
WHERE brn > 1 OR
(brn = 1 AND type > 1309) OR
(brn = 1 AND type = 1309 AND num > 2)
ORDER BY brn DESC, type DESC, num DESC;

我建议您使用"math":将列按"相关"顺序排列,然后相乘以容纳单个字段中的所有数字。例如:new_id=first_column*100000+second_column*10+third_column

所以在第1行的情况下,代码是1313091。数学比字符串操作快得多。然后,您将能够查询将键除以所需的部分。最终,在您认为会增长很多的列中再添加一些零。

在构建具有列的键时,会按照外部所需的顺序保留顺序。

所以你的查询会像:

select *
from table
where new_id > (branch)*100000+(type)*10+number 

IDEA是这样的:

123456 99349199 12341234 : having enough space for a 'reasonable' amount of numbers
000000 00000000 00000000
so 123456 x 1 00000000 00000000  = 123456 00000000 00000000  +
99349199 x 1 00000000         =        99349199 00000000  +
and
12341234 x                      1 =            12341234  =                  
-------------------------
123456 99349199 12341234

更新:

select *
from my_table
WHERE new_id (branch)*1 00000000 00000000+(type)*1 00000000+number 

显然,我们的"new_label"定义发生了变化:

new_label= (branch)*100000000 00000000+(type)*100000000 + number

所以你可以拥有这些最大长度:

<all digits you want> (branch) + 8 digit (type) + 8 digit (number)

我建议您添加数据库触发器以强制执行这些最大长度的

在数据库中创建此函数

CREATE FUNCTION dbo.GetCompositeKey(@branch int, @type int, @number int)
RETURNS bigint
AS
BEGIN
RETURN cast(@branch as bigint) * 100000000000 + cast(@type as bigint) * 10000000 + @number
END

然后在比较时使用它,如下所示:

SELECT TOP 1 * FROM [TABLE]
WHERE dbo.GetCompositeKey(branch, type, number) > 
dbo.GetCompositeKey(13, 1309, 2)        
ORDER BY dbo.GetCompositeKey(branch, type, number)

注意:为了便于说明,这个查询被简化了。当从C#执行它时,应该使用参数化查询向SQL函数提供3个整数,以避免SQL注入攻击。


EDIT:在发布这篇文章后,我读到了一些关于其他答案的评论,你说你不懂数学。到目前为止,在大多数答案中,方法只是创建一个大整数,将所有3个值(分支、类型和数字)组合在一起,以创建一个可比较的值。事实上,这与您在问题('0013130900002')中使用基于字符串的组合键进行字符串连接所做的操作相同。正如另一个答案所指出的,数学方法应该更快。字符串中的另一个错误是零,将@number字段填充为仅5位数字,而您已经声明它必须能够容纳7位数字。

恕我直言,到目前为止,所有的答案都使用了非常基本的数学。如果你不懂基本的数学,你就不可能成为一名程序员,所以你应该把提高这项技能作为首要任务。

您在对其他答案的评论中说过,您不想填充数据集或使用MoveNext等。另一种方法是创建一个包含复合键的视图。

首先,创建我在另一个答案中定义的函数

然后,创建视图

CREATE VIEW [myview]
AS
SELECT 
*,
dbo.GetCompositeKey(branch, type, number) as CompositeKey
FROM [table]

这样查询:

SELECT TOP 1 * FROM [myview] 
WHERE CompositeKey > dbo.GetCompositeKey(13, 1309, 2)        
ORDER BY CompositeKey

与我的其他答案相比,这种方法可能会提高查询性能。你需要测试它才能找到答案。

最新更新