Array
(
)

Ordenar campo texto como numérico.

Rc.salvador
   - 02 mai 2005

Tenho uma tabela que armazena endereços IP e preciso ordená-las pelos mesmos. Entretanto isso causa um grande problema, pois os valores ficam ordenados à partir do código alfabético do caracter e não do valore real, por exemplo:

172.16.0.10 <<======
172.16.0.100
172.16.0.101
172.16.0.102
172.16.0.11 <<======
172.16.0.110
172.16.0.111

Há alguma maneira de efetuar esta ordem de maneira coerente ? Tentei utilizar ORDER BY CAST(IP AS Numeric) mas não funciona devido aos pontos. Tentei também desmontar o IP utilizando SUBSTRING e CHARINDEX mas ficou muito complicado por causa tmb dos pontos.

Gandalf.nho
   - 02 mai 2005

Enquanto permanecer como texto ele irá ordenar alfabeticamente e não numericamente, a menos que todos os IPs tenham o mesmo tamanho e os pontos nos mesmos lugares (o que não dá)

Tinorj
   - 27 nov 2006

Uma solução seria armazenar cada numero separado e fazer um campo calculado exibindo o IP já concatenado com os pontos, mas não sei qual seria esse custo para sua situação.

Abaixo segue um exemplo.

#Código

use Northwind

GO

if exists (select * from dbo.sysobjects where id = object_id(N´[dbo].[IPs]´) and OBJECTPROPERTY(id, N´IsUserTable´) = 1)
drop table [dbo].[IPs]
GO

CREATE TABLE [dbo].[IPs] (
[PARTE1] [int] NULL ,
[PARTE2] [int] NULL ,
[PARTE3] [int] NULL ,
[PARTE4] [int] NULL ,
[IP] AS (CONVERT(varchar(3),[PARTE1]) + ´.´ + CONVERT(varchar(3),[PARTE2]) + ´.´ + CONVERT(varchar(3),[PARTE3]) + ´.´ + CONVERT(varchar(3),[PARTE4]))
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[IPs] ADD 
CONSTRAINT [DF_IPs_PARTE1] DEFAULT (0) FOR [PARTE1],
CONSTRAINT [DF_IPs_PARTE2] DEFAULT (0) FOR [PARTE2],
CONSTRAINT [DF_IPs_PARTE3] DEFAULT (0) FOR [PARTE3],
CONSTRAINT [DF_IPs_PARTE4] DEFAULT (0) FOR [PARTE4],
CONSTRAINT [CK_IP_PARTE1] CHECK ([PARTE1] >= 0 and [PARTE1] < 255),
CONSTRAINT [CK_IP_PARTE2] CHECK ([PARTE2] >= 0 and [PARTE2] < 255),
CONSTRAINT [CK_IP_PARTE3] CHECK ([PARTE3] >= 0 and [PARTE3] < 255),
CONSTRAINT [CK_IP_PARTE4] CHECK ([PARTE4] >= 0 and [PARTE4] < 255)
GO


GO

INSERT INTO IPs VALUES (172,16,0,10)
INSERT INTO IPs VALUES (172,16,0,100)
INSERT INTO IPs VALUES (172,16,0,101)
INSERT INTO IPs VALUES (172,16,0,102)
INSERT INTO IPs VALUES (172,16,0,11)
INSERT INTO IPs VALUES (172,16,0,110)
INSERT INTO IPs VALUES (172,16,0,111)

GO

SELECT IP FROM IPs
ORDER BY PARTE1, PARTE2, PARTE3, PARTE4  



Psergio.p
   - 03 dez 2006

Minha sugestão é a seguinte:

Criar uma UDF para retornar as partes do IP, veja abaixo o exemplo:

CREATE FUNCTION UDF_GETIP(@ENDERECO_IP VARCHAR(15),@PARTE INT)
RETURNS INT
AS
BEGIN
DECLARE @PARTE01 INT
DECLARE @PARTE02 INT
DECLARE @PARTE03 INT
DECLARE @PARTE04 INT
DECLARE @POSICAO INT

SET @POSICAO = CHARINDEX(´.´,@ENDERECO_IP)
SET @PARTE01 = SUBSTRING(@ENDERECO_IP,1,@POSICAO - 1)
SET @ENDERECO_IP = SUBSTRING(@ENDERECO_IP,@POSICAO + 1, LEN(@ENDERECO_IP))

SET @POSICAO = CHARINDEX(´.´,@ENDERECO_IP)
SET @PARTE02 = SUBSTRING(@ENDERECO_IP,1,@POSICAO - 1)
SET @ENDERECO_IP = SUBSTRING(@ENDERECO_IP,@POSICAO + 1, LEN(@ENDERECO_IP))

SET @POSICAO = CHARINDEX(´.´,@ENDERECO_IP)
SET @PARTE03 = SUBSTRING(@ENDERECO_IP,1,@POSICAO - 1)
SET @ENDERECO_IP = SUBSTRING(@ENDERECO_IP,@POSICAO + 1, LEN(@ENDERECO_IP))

SET @PARTE04 = SUBSTRING(@ENDERECO_IP,1,@POSICAO - 1)

IF @PARTE = 1 BEGIN
RETURN(@PARTE01)
END
IF @PARTE = 2 BEGIN
RETURN(@PARTE02)
END
IF @PARTE = 3 BEGIN
RETURN(@PARTE03)
END
IF @PARTE = 4 BEGIN
RETURN(@PARTE04)
END
RETURN 0
END

Depois veja como usar a função e order os valores:

SELECT IP FROM(
SELECT *,DBO.UDF_GETIP(IP,1) AS IP1,DBO.UDF_GETIP(IP,2) AS IP2,DBO.UDF_GETIP(IP,3) AS IP3,
DBO.UDF_GETIP(IP,4) AS IP4 FROM EnderecoIP) AS SQ
ORDER BY IP1,IP2,IP3,IP4

Espero que ajude, me dá um feedback depois!

[]
Paulo