Eu lidei com isso extensivamente, e minha filosofia geral é usar o método de frequência de uso. É complicado, mas permite que você execute ótimas análises sobre os dados:
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;
Como regra geral, você terá Caminhos semelhantes em um único Domínio, mas QueryStrings diferentes para cada caminho.
Eu originalmente projetei isso para ter todas as partes indexadas em uma única tabela (Protocol, Domain, Path, Query String), mas acho que o acima ocupa menos espaço e é melhor para obter melhores dados.
text
tende a ser lento, então você pode alterar "Path" para um varchar após algum uso. A maioria dos servidores morre após cerca de 1K por um URL, mas eu já vi alguns grandes e erraria por não perder dados. Sua consulta de recuperação é complicada, mas se você a abstrair em seu código, não há 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;
Armazene um número de porta se não for padrão (não-80 para http, não-443 para https), caso contrário, armazene-o como NULL para significar que não deve ser incluído. (Você pode adicionar a lógica ao MySQL, mas fica muito mais feio.)
Eu sempre (ou nunca) tiraria o "/" do Caminho, assim como o "?" do QueryString para economia de espaço. Só a perda seria capaz de distinguir entre
http://www.example.com/
http://www.example.com/?
O que, se for importante, eu mudaria sua tática para nunca tirá-lo e apenas incluí-lo. Tecnicamente,
http://www.example.com
http://www.example.com/
São os mesmos, portanto, remover a barra Path está sempre OK.
Então, para analisar:
http://www.example.com/my/path/to/my/file.php?id=412&crsource=google+adwords
Usaríamos algo como
parse_url
em PHP para produzir:array(
[scheme] => 'http',
[host] => 'www.example.com',
[path] => '/my/path/to/my/file.php',
[query] => 'id=412&crsource=google+adwords',
)
Você então verificaria/inseriria (com bloqueios apropriados, não mostrados):
SELECT D.ID FROM Domain D
WHERE
D.Protocol=0
AND D.Domain='www.example.com'
AND D.Port IS NULL
(se não existir)
INSERT INTO Domain (
Protocol, Domain, Port
) VALUES (
0, 'www.example.com', NULL
);
Temos então nosso
$DomainID
daqui para frente... Em seguida, insira no DomainPath:
SELECT DP.ID FORM DomainPath DP WHERE
DP.Domain=$DomainID AND Path='/my/path/to/my/file.php';
(se não existir, insira-o da mesma forma)
Temos então nosso
$DomainPathID
daqui para frente... SELECT U.ID FROM URL
WHERE
DomainPath=$DomainPathID
AND QueryString='id=412&crsource=google+adwords'
e insira se necessário.
Agora, deixe-me observar importante , que o esquema acima será lento para sites de alto desempenho. Você deve modificar tudo para usar um hash de algum tipo para acelerar
SELECT
s. Em suma, a técnica é como: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'));
Eu deliberadamente o eliminei do acima para mantê-lo simples, mas comparar um TEXT com outro TEXT para seleções é lento e quebra para strings de consulta muito longas. Não use um índice de tamanho fixo porque isso também quebrará. Para strings de comprimento arbitrário onde a precisão é importante, uma taxa de falha de hash é aceitável.
Finalmente, se você puder, faça o lado do cliente de hash MD5 para salvar o envio de blobs grandes ao servidor para fazer a operação MD5. A maioria das linguagens modernas suporta MD5 integrado:
SELECT ID FROM Foo WHERE Hash=UNHEX('82fd4bcf8b686cffe81e937c43b5bfeb');
Mas eu discordo.