3

作为一个简化的示例,假设我正在销售小部件。我在全国范围内销售它们(在美国和加拿大),但有些只能在某些地区(美国的一个或多个州或加拿大省)销售。

我想要一种存储此信息的好方法,以及一种快速查询给定用户可用的小部件的方法。“美国、50 个州和华盛顿特区”是最常见的值,所以我宁愿不插入 51 行。

MySQL 不支持位图索引,因此排除了这一点。

以下是一些组合:

  • 美国 50 个州和哥伦比亚特区
  • 美国 50 个州、哥伦比亚特区、加拿大,但不包括魁北克。
  • 美国 48 个州和华盛顿特区
  • 美国,DC,但不是科罗拉多
  • 美国、哥伦比亚特区和领地(波多黎各等)。

我的用户将为他们的州/省和国家/地区提供一个值。

你能推荐一个提供良好存储和快速匹配的模式吗?

谢谢!

4

2 回答 2

1

您应该构建预定义的值集并将此集存储到项目中。使用值可以检索匹配集和匹配项。

CREATE TABLE `valuesets` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `valueset_items` (
  `valueset_id` int(11) unsigned NOT NULL,
  `value` varchar(20) NOT NULL DEFAULT '',
  PRIMARY KEY (`valueset_id`,`value`),
  CONSTRAINT `fk_valueset_items_valueset` FOREIGN KEY (`valueset_id`) REFERENCES `valuesets` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `items` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL DEFAULT '',
  `valueset_id` int(11) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_items_valueset` (`valueset_id`),
  CONSTRAINT `fk_items_valueset` FOREIGN KEY (`valueset_id`) REFERENCES `valuesets` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

选择与特殊值匹配的所有项目

SELECT *
FROM items
WHERE 
  valueset_id IN ( SELECT valueset_id 
                   FROM valueset_items 
                   WHERE `value` = 'A' )

SQL 小提琴演示

于 2012-12-22T07:36:17.140 回答
0

这是一种 MySQL SET类型,假设您可以将数据集减少到 64 个项目(或者,根据其他条件使用多个集合)。


我想我会扩展我的答案,因为我认为有些人只是不了解集合的力量。示例表:

CREATE TABLE `Test` (
  `setid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `setname` varchar(64) NOT NULL,
  `setstate` set('AK','AL','AR','AZ','CA','CO','CT','DC','DE','FL','GA','HI','IA','ID','IL','IN','KS','KY','LA','MA','MD','ME','MI','MN','MO','MS','MT','NC','ND','NE','NH','NJ','NM','NV','NY','OH','OK','OR','PA','RI','SC','SD','TN','TX','UT','VA','VT','WA','WI','WV','WY') NOT NULL,
  PRIMARY KEY (`setid`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;

insert into `Test` values('1','test','AZ,CA,NJ,NM,NY,VA,VT');

请注意,我们对状态使用单个集合字段。更复杂的用途可能需要使用多个集合,但每条记录稍微水平的 qword 可能比在查找表上添加大量的额外连接操作更便宜,而查找表上很容易达到大量记录。

以下是 3 个(功能上)等效的拉动。请注意,位掩码是提取此数据的最快方法:

SELECT * FROM Test WHERE setstate & 1000;

对于测试 #1,我们使用 1000 作为位掩码,因为这对应于我们列表 (AZ) 中的项目 #4。到目前为止,这是最快的方法......并且存储这些数据的方法很少,可以为您提供更快的结果潜力。

SELECT * FROM Test WHERE setstate LIKE '%AZ%';

这种方法可以使用索引,但是由于模糊匹配会有些慢。

SELECT * FROM Test WHERE FIND_IN_SET('AZ',setstate);

这种方法将比模糊匹配更快,但它的本质在大多数实际使用中几乎需要使用临时表。

于 2012-12-22T05:48:17.623 回答