Jag har behandlat detta mycket, och min allmänna filosofi är att använda metoden för användningsfrekvens. Det är besvärligt, men det låter dig köra några fantastiska analyser på data:
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;
Som en allmän regel kommer du att ha liknande sökvägar på en enskild domän, men olika QueryStrings för varje sökväg.
Jag designade ursprungligen detta för att ha alla delar indexerade i en enda tabell (protokoll, domän, sökväg, frågesträng) men tycker att ovanstående är mindre utrymmeskrävande och ger bättre tillgång till bättre data.
text
tenderar att vara långsam, så du kan ändra "Path" till en varchar efter viss användning. De flesta servrar dör efter cirka 1K för en URL, men jag har sett några stora och skulle ta fel på sidan om att inte förlora data.
Din hämtningsfråga är besvärlig, men om du abstraherar bort den i din kod är det inga problem:
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;
Lagra ett portnummer om det inte är standard (icke-80 för http, icke-443 för https), annars lagras det som NULL för att visa att det inte ska inkluderas. (Du kan lägga till logiken i MySQL men det blir mycket fulare.)
Jag skulle alltid (eller aldrig) ta bort "/" från vägen såväl som "?" från QueryString för utrymmesbesparingar. Endast förlust skulle kunna skilja mellan
http://www.example.com/
http://www.example.com/?
Vilket, om det är viktigt, så skulle jag ändra ditt grepp för att aldrig ta bort det och bara inkludera det. Tekniskt sett,
http://www.example.com
http://www.example.com/
Är desamma, så att ta bort snedstrecket Path är alltid OK.
Så, för att analysera:
http://www.example.com/my/path/to/my/file.php?id=412&crsource=google+adwords
Vi skulle använda något som parse_url
i PHP för att producera:
array(
[scheme] => 'http',
[host] => 'www.example.com',
[path] => '/my/path/to/my/file.php',
[query] => 'id=412&crsource=google+adwords',
)
Du skulle då kontrollera/sätta in (med lämpliga lås, visas inte):
SELECT D.ID FROM Domain D
WHERE
D.Protocol=0
AND D.Domain='www.example.com'
AND D.Port IS NULL
(om det inte finns)
INSERT INTO Domain (
Protocol, Domain, Port
) VALUES (
0, 'www.example.com', NULL
);
Vi har då vårt $DomainID
framöver...
Infoga sedan i DomainPath:
SELECT DP.ID FORM DomainPath DP WHERE
DP.Domain=$DomainID AND Path='/my/path/to/my/file.php';
(om det inte finns, infoga det på samma sätt)
Vi har då vårt $DomainPathID
framöver...
SELECT U.ID FROM URL
WHERE
DomainPath=$DomainPathID
AND QueryString='id=412&crsource=google+adwords'
och infoga vid behov.
Låt mig nu notera viktigt , att ovanstående schema kommer att vara långsamt för högpresterande webbplatser. Du bör modifiera allt för att använda en hash av något slag för att snabba upp SELECT
s. Kort sagt, tekniken är som:
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'));
Jag har medvetet eliminerat det från ovan för att hålla det enkelt, men att jämföra en TEXT med en annan TEXT för markeringar är långsam och går sönder för riktigt långa frågesträngar. Använd inte heller ett index med fast längd eftersom det också kommer att gå sönder. För strängar med godtycklig längd där noggrannhet spelar roll, är en hash-felfrekvens acceptabel.
Slutligen, om du kan, gör MD5-hash-klientsidan för att spara att skicka stora blobbar till servern för att utföra MD5-operationen. De flesta moderna språk stöder MD5 inbyggt:
SELECT ID FROM Foo WHERE Hash=UNHEX('82fd4bcf8b686cffe81e937c43b5bfeb');
Men jag avviker.