Mi sono occupato di questo ampiamente e la mia filosofia generale è quella di utilizzare il metodo della frequenza d'uso. È ingombrante, ma ti consente di eseguire ottime analisi sui dati:
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;
Come regola generale, avrai percorsi simili su un singolo dominio, ma QueryStrings differenti per ogni percorso.
Inizialmente l'ho progettato per avere tutte le parti indicizzate in un'unica tabella (protocollo, dominio, percorso, stringa di query), ma penso che quanto sopra sia meno dispendioso in termini di spazio e si presta meglio a ottenere dati migliori da esso.
text
tende ad essere lento, quindi puoi cambiare "Percorso" in un varchar dopo un po' di utilizzo. La maggior parte dei server muore dopo circa 1K per un URL, ma ne ho visti alcuni di grandi dimensioni e sbaglierei per non perdere dati.
La tua query di recupero è ingombrante, ma se la astrai nel tuo codice, nessun problema:
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;
Memorizza un numero di porta se non è standard (non 80 per http, non 443 per https), altrimenti memorizzalo come NULL per indicare che non dovrebbe essere incluso. (Puoi aggiungere la logica a MySQL ma diventa molto più brutto.)
Toglierei sempre (o mai) la "/" dal Percorso così come la "?" da QueryString per risparmiare spazio. Solo la perdita sarebbe in grado di distinguere tra
http://www.example.com/
http://www.example.com/?
Il che, se importante, cambierei il tuo approccio per non rimuoverlo mai e includerlo. Tecnicamente,
http://www.example.com
http://www.example.com/
Sono gli stessi, quindi rimuovere la barra del percorso è sempre OK.
Quindi, per analizzare:
http://www.example.com/my/path/to/my/file.php?id=412&crsource=google+adwords
Useremmo qualcosa come parse_url
in PHP per produrre:
array(
[scheme] => 'http',
[host] => 'www.example.com',
[path] => '/my/path/to/my/file.php',
[query] => 'id=412&crsource=google+adwords',
)
Dovresti quindi controllare/inserire (con i blocchi appropriati, non mostrati):
SELECT D.ID FROM Domain D
WHERE
D.Protocol=0
AND D.Domain='www.example.com'
AND D.Port IS NULL
(se non esiste)
INSERT INTO Domain (
Protocol, Domain, Port
) VALUES (
0, 'www.example.com', NULL
);
Abbiamo quindi il nostro $DomainID
andando avanti...
Quindi inserisci in DomainPath:
SELECT DP.ID FORM DomainPath DP WHERE
DP.Domain=$DomainID AND Path='/my/path/to/my/file.php';
(se non esiste, inserirlo allo stesso modo)
Abbiamo quindi il nostro $DomainPathID
andando avanti...
SELECT U.ID FROM URL
WHERE
DomainPath=$DomainPathID
AND QueryString='id=412&crsource=google+adwords'
e inserirlo se necessario.
Ora, lasciatemi notare importante , che lo schema di cui sopra sarà lento per i siti ad alte prestazioni. Dovresti modificare tutto per usare un hash di qualche tipo per velocizzare SELECT
S. In breve, la tecnica è come:
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'));
L'ho deliberatamente eliminato da quanto sopra per mantenerlo semplice, ma confrontare un TESTO con un altro TESTO per le selezioni è lento e si interrompe per stringhe di query molto lunghe. Non utilizzare nemmeno un indice a lunghezza fissa perché anche quello si romperà. Per stringhe di lunghezza arbitraria in cui la precisione è importante, è accettabile un tasso di errore dell'hash.
Infine, se puoi, esegui il lato client hash MD5 per salvare l'invio di BLOB di grandi dimensioni al server per eseguire l'operazione MD5. La maggior parte delle lingue moderne supporta MD5 integrato:
SELECT ID FROM Foo WHERE Hash=UNHEX('82fd4bcf8b686cffe81e937c43b5bfeb');
Ma sto divagando.