【问题标题】:Condensing Rows in SQL ServerSQL Server 中的压缩行
【发布时间】:2017-07-12 18:26:01
【问题描述】:

假设我有一个表,对于每一个新的“购买的​​物品”,它都会重新创建一个具有所有相同值的行,但新物品除外,并且有 1000 种不同的物品。

ID      Time    Date        Cashier Item Bought     
0001    12:00   16/1/26     Bob     Apple
0001    12:00   16/1/26     Bob     chicken
0001    12:00   16/1/26     Bob     eggs
0001    12:00   16/1/26     Bob     Banana
0002    12:30   16/1/27     Steve   Apple
0002    12:30   16/1/27     Steve   pork
0002    12:30   16/1/27     Steve   milk

我想要做的是以节省空间但仍允许我按项目搜索交易的方式压缩行。

我尝试过的事情

位操作

将每个位分配给一个项目。

Item    Bit
Apple   1 (0000 0001)
eggs    2 (0000 0010)
Chicken 3 (0000 0100)
fish    4
Banana  5
pork    6
milk    7
.
.
.
(to 1000)

改变

ID      Time    Date        Cashier Item Bought 
0001    12:00   16/1/26     Bob     Apple
0001    12:00   16/1/26     Bob     chicken
0001    12:00   16/1/26     Bob     eggs
0001    12:00   16/1/26     Bob     Banana 

进入

ID      Time    Date        Cashier     Items Bought                    
0001    12:00   16/1/26     Bob         0001 0111

我可以进行按位运算来检查项目。

检查交易是否有苹果和香蕉

0001 0111   Items Bought
0001 0001   Check = bits for apple and banana 

逻辑:if (~Check nor Items Bought == 0)(包括)

1110 1110   ~Check
0001 0111   Items Bought

0000 0000   

问题是您只能使用 BIGINT 进行最多 64 位的按位运算。我需要 1000 位或 16 个额外的列。

质数

根据购买物品的频率分配质数

Item    Bought  Assigned Prime
Apple   190893  2
eggs    150022  3
Chicken 71026   5
fish    59827   7
Banana  10274   11
pork    5271    13
milk    1021    17
.
.
.
(to 1000)

对于每一列,将每个素数相乘

改变

ID      Time    Date        Cashier Item Bought 
0001    12:00   16/1/26     Bob     Apple
0001    12:00   16/1/26     Bob     chicken
0001    12:00   16/1/26     Bob     eggs
0001    12:00   16/1/26     Bob     Banana
0002    12:30   16/1/27     Steve   Apple
0002    12:30   16/1/27     Steve   pork
0002    12:30   16/1/27     Steve   milk

ID      Time    Date        Cashier     Items Bought
0001    12:00   16/1/26     Bob         330 (2 * 5 * 3 * 11)
0002    12:30   16/1/27     Steve       442 (2 * 13 * 17)

如果我想查看哪些交易有项目,请将列除以该素数。根据素数的性质,如果结果是整数,则交易确实具有该项目。

检查交易是否有苹果和香蕉

primeCheck = 22 (2 * 11)

逻辑:如果(购买的商品 % primeCheck == 0)(包括)

ID      Time    Date        Cashier     Include
0001    12:00   16/1/26     Bob         YES     330 % 22 = 0 
0002    12:30   16/1/27     Steve       NO      442 % 22 = 2 

问题是,质数相乘变得非常快。我会使用这种过度位操作的原因是,对于 BM,无论项目如何,我都需要 1024 位。

字符串连接

改变

ID      Time    Date        Cashier Item Bought 
0001    12:00   16/1/26     Bob     Apple
0001    12:00   16/1/26     Bob     chicken
0001    12:00   16/1/26     Bob     eggs
0001    12:00   16/1/26     Bob     Banana 

进入

ID      Time    Date        Cashier     Items Bought                    
0001    12:00   16/1/26     Bob         Apple, chicken, eggs, Banana    

问题是遍历每个购买的商品字符串的搜索复杂性。

有什么建议吗?

【问题讨论】:

  • 这对我来说毫无意义。您是否尝试创建以逗号分隔的已购买项目编号列表?还是完全不同的东西?对我来说,这听起来像是一个经典的 XY 问题。
  • 我认为你应该表达你想要做的事情的逻辑,让SQL引擎处理底层细节。
  • 啊,我明白了。好吧,让我解决我的问题。简而言之,我试图以节省时间和计算的方式压缩我的表格
  • 在查询您要解决的表时,您是否真的遇到了性能问题?还是您只是想积极主动地提出解决方案来解决不存在的性能问题?
  • 我遇到了性能问题,但是当我对此进行更多研究时,我明白了你的意思。我的查询运行不到一分钟,直到我必须加入这个需要 20 多分钟的表。我对 SQL 比较陌生,并且假设压缩此表中的数据将导致更平滑的连接,因为我不会处理庞大的数量,但它可能不是我正在寻找的解决方案。

标签: sql-server primes


【解决方案1】:

为什么不简单地规范化表结构呢? 可能类似于以下内容...

USE tempdb;
GO

CREATE TABLE dbo.Personnel (
    PersonID INT NOT NULL
        CONSTRAINT pk_Personnel_PersonID PRIMARY KEY CLUSTERED (PersonID),
    PersonName VARCHAR(50) NOT NULL
    );

CREATE TABLE dbo.Product (
    ProductID INT NOT NULL
        CONSTRAINT pk_Product_ProductID PRIMARY KEY CLUSTERED (ProductID),
    ProductName VARCHAR(50) NOT NULL
    );

CREATE TABLE dbo.OrderHeader (
    OrderHeaderID INT NOT NULL 
        CONSTRAINT pk_OrderHeader_OrderHeaderID PRIMARY KEY CLUSTERED (OrderHeaderID),
    OrderDT DATETIME2(0) NOT NULL
        CONSTRAINT df_OrderHeader_OrderDT DEFAULT (GETDATE()),
    CashierID INT NOT NULL
        CONSTRAINT fk_OrderHeader_Personnel_PersonID FOREIGN KEY REFERENCES dbo.Personnel(PersonID)
    );

CREATE TABLE dbo.OrderDetail (
    OrderDetailID INT NOT NULL
        CONSTRAINT pk_OrderDetail_OrderDetailID PRIMARY KEY CLUSTERED (OrderDetailID),
    OrderHeaderID INT NOT NULL
        CONSTRAINT fk_OrderDetail_OrderHeader_OrderHeaderID FOREIGN KEY REFERENCES dbo.OrderHeader(OrderHeaderID),
    ProductID INT NOT NULL
        CONSTRAINT fk_OrderDetail_Product_ProductID FOREIGN KEY REFERENCES dbo.Product(ProductID),
    Quantity INT NOT NULL
        CONSTRAINT df_OrderDetail_Quantity DEFAULT (1),
    Returned BIT NOT NULL
        CONSTRAINT df_OrderDetail_Returned DEFAULT(0)
    );

INSERT dbo.Personnel (PersonID, PersonName) VALUES (1, 'Bob');

INSERT dbo.Product (ProductID, ProductName) VALUES 
    (1, 'Apple'), (2, 'Chicken'), (3, 'Eggs'), 
    (4, 'Banana'), (5, 'Pork'), (6, 'Milk');

INSERT dbo.OrderHeader (OrderHeaderID, OrderDT, CashierID) VALUES 
    (1, '2016-01-26 12:00:00', 1), (2, '2016-01-26 12:00:00', 1);

INSERT dbo.OrderDetail (OrderDetailID, OrderHeaderID, ProductID, Quantity, Returned) VALUES
    (1, 1, 1, 1, 1), (2, 1, 2, 1, 0), (3, 1, 3, 12, 0), (4, 1, 4, 1, 0),
    (5, 2, 1, 2, 0), (6, 2, 5, 1, 1), (7, 2, 6, 1, 0);

--===============================================================

-- query that produces the original data...
SELECT 
    oh.OrderHeaderID,
    [Time] = CAST(oh.OrderDT AS TIME),
    [Date] = CAST(oh.OrderDT AS DATE),
    Cashier = p.PersonName,
    ItemBought = p2.ProductName,
    od.Returned
FROM 
    dbo.OrderHeader oh
    JOIN dbo.OrderDetail od
        ON oh.OrderHeaderID = od.OrderHeaderID
    JOIN dbo.Personnel p
        ON oh.CashierID = p.PersonID
    JOIN dbo.Product p2
        ON od.ProductID = p2.ProductID;

【讨论】:

    猜你喜欢
    • 2019-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多