Занимавал съм се подробно с това и моята обща философия е да използвам метода на честотата на употреба. Това е тромаво, но ви позволява да извършвате страхотни анализи на данните:
CREATE TABLE URL (
ID integer unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
DomainPath integer unsigned NOT NULL,
QueryString text
) Engine=MyISAM;
CREATE TABLE DomainPath (
ID integer unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
Domain integer unsigned NOT NULL,
Path text,
UNIQUE (Domain,Path)
) Engine=MyISAM;
CREATE TABLE Domain (
ID integer unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
Protocol tinyint NOT NULL,
Domain varchar(64)
Port smallint NULL,
UNIQUE (Protocol,Domain,Port)
) Engine=MyISAM;
Като общо правило ще имате подобни пътища в един домейн, но различни QueryStrings за всеки път.
Първоначално проектирах това, за да има всички части, индексирани в една таблица (протокол, домейн, път, низ на заявка), но смятам, че горното е по-малко пространство-интензивно и дава възможност за по-добро извличане на по-добри данни от него.
text
има тенденция да е бавен, така че можете да промените "Path" на varchar след известно използване. Повечето сървъри умират след около 1K за URL, но съм виждал някои големи и бих сгрешил, за да не загубя данни.
Вашата заявка за извличане е тромава, но ако я абстрахирате в кода си, няма проблем:
SELECT CONCAT(
IF(D.Protocol=0,'http://','https://'),
D.Domain,
IF(D.Port IS NULL,'',CONCAT(':',D.Port)),
'/', DP.Path,
IF(U.QueryString IS NULL,'',CONCAT('?',U.QueryString))
)
FROM URL U
INNER JOIN DomainPath DP ON U.DomainPath=DP.ID
INNER JOIN Domain D on DP.Domain=D.ID
WHERE U.ID=$DesiredID;
Съхранявайте номер на порт, ако е нестандартен (не-80 за http, не-443 за https), в противен случай го съхранете като NULL, за да означава, че не трябва да бъде включен. (Можете да добавите логиката към MySQL, но тя става много по-грозна.)
Винаги (или никога) бих премахнал "/" от пътя, както и "?" от QueryString за спестяване на място. Само загубата би могла да направи разлика между
http://www.example.com/
http://www.example.com/?
Което, ако е важно, тогава бих променил вашата тактика, за да не го събличате и просто да го включите. Технически,
http://www.example.com
http://www.example.com/
Същите са, така че премахването на наклонената черта на пътя винаги е ОК.
И така, за да анализирам:
http://www.example.com/my/path/to/my/file.php?id=412&crsource=google+adwords
Ще използваме нещо като parse_url
в PHP, за да произведе:
array(
[scheme] => 'http',
[host] => 'www.example.com',
[path] => '/my/path/to/my/file.php',
[query] => 'id=412&crsource=google+adwords',
)
След това ще проверите/вмъкнете (с подходящи ключалки, не е показано):
SELECT D.ID FROM Domain D
WHERE
D.Protocol=0
AND D.Domain='www.example.com'
AND D.Port IS NULL
(ако не съществува)
INSERT INTO Domain (
Protocol, Domain, Port
) VALUES (
0, 'www.example.com', NULL
);
След това имаме нашия $DomainID
продължавам напред...
След това вмъкнете в DomainPath:
SELECT DP.ID FORM DomainPath DP WHERE
DP.Domain=$DomainID AND Path='/my/path/to/my/file.php';
(ако не съществува, поставете го по подобен начин)
След това имаме нашия $DomainPathID
продължавам напред...
SELECT U.ID FROM URL
WHERE
DomainPath=$DomainPathID
AND QueryString='id=412&crsource=google+adwords'
и поставете, ако е необходимо.
Сега, нека отбележа важното , че горната схема ще бъде бавна за високопроизводителни сайтове. Трябва да промените всичко, за да използвате някакъв хеш, за да ускорите SELECT
с. Накратко, техниката е като:
CREATE TABLE Foo (
ID integer unsigned PRIMARY KEY NOT NULL AUTO_INCREMENT,
Hash varbinary(16) NOT NULL,
Content text
) Type=MyISAM;
SELECT ID FROM Foo WHERE Hash=UNHEX(MD5('id=412&crsource=google+adwords'));
Умишлено го елиминих от горното, за да го опростя, но сравняването на ТЕКСТ с друг ТЕКСТ за избор е бавно и прекъсва за наистина дълги низове на заявка. Не използвайте и индекс с фиксирана дължина, защото това също ще се счупи. За низове с произволна дължина, при които точността има значение, честотата на неуспех на хеширане е приемлива.
И накрая, ако можете, направете MD5 хеш клиентската страна, за да спестите изпращане на големи петна до сървъра, за да извършите операцията MD5. Повечето съвременни езици поддържат вградения MD5:
SELECT ID FROM Foo WHERE Hash=UNHEX('82fd4bcf8b686cffe81e937c43b5bfeb');
Но се отклонявам.