2010-05-05 13 views
12

Necesito una solución para una consulta de selección en Sql Server 2005.¿Hay alguna manera de dividir los resultados de una consulta de selección en dos partes iguales?

Me gustaría tener una consulta que devuelva dos ResultSets cada uno de los cuales contiene exactamente la mitad de todos los registros que coinciden con un cierto criterio. Intenté usar TOP 50 PERCENT junto con un Order By pero si el número de registros en la tabla es impar, aparecerá un registro en ambos sets de resultados. No quiero tener ningún registro duplicado en los conjuntos de registros. Ejemplo:

Tengo una tabla simple con los campos TheID (PK) y TheValue (varchar (10)) y 5 registros. Omita la cláusula where por ahora.

SELECT TOP 50 PERCENT * FROM TheTable ORDER BY TheID asc 

resultados en 1,2,3

SELECT TOP 50 PERCENT * FROM TheTable ORDER BY TheID desc 

los resultados de la ID seleccionada en el 3,4,5

3 de ID seleccionado es un dup. En la vida real, por supuesto, las consultas son bastante complicadas con un montón de cláusulas where y subconsultas.

+0

Asumo que esto es SQL Server? –

+1

¿Quién está consumiendo estas dos consultas? Si ambos consumidores se conocen (presumiblemente por sus resultados), ¿por qué no buscar toda la lista y dividirla entre los consumidores para que no haya duplicados? –

+0

sí. lo siento, olvidé mencionarlo. – Mats

Respuesta

34

SQL Server 2005 y similares:

select *, ntile(2) over(order by theid) as tile_nr from thetable 

ntile(n) asigna la salida en n segmentos, cada uno del mismo tamaño (más o menos el redondeo cuando el número de filas no es divisible por n). Así que esto produce la salida:

1 | value1 | 1 
2 | value2 | 1 
3 | value3 | 1 
4 | value4 | 2 
5 | value5 | 2 

Si lo que desea es la parte superior o la mitad inferior, es necesario poner esto en una subconsulta, por ejemplo:

select theid, thevalue from (
    select theid, thevalue, ntile(2) over(order by theid) as tile_nr from thetable 
) x 
where x.tile_nr = 1 

devolverá la mitad superior, y de manera similar usar x.tile_nr = 2 para la mitad inferior

+1

+1, lo probé en mi sistema. @Thomas, no hay 'partition by', solo' order by' –

+2

@KM - Sí, probado en mi sistema también. Esta es para mí la solución más inteligente que asume SQL2k5 +. – Thomas

+0

+1 ¡una de las funciones de clasificación más a menudo olvidadas, de hecho! Gran respuesta –

6

Usted podría utilizar estas dos consultas:

SELECT * FROM (
    SELECT *, ROW_NUMBER() OVER (ORDER BY TheID) AS rn FROM TheTable 
) T1 
WHERE rn % 2 = 0 

SELECT * FROM (
    SELECT *, ROW_NUMBER() OVER (ORDER BY TheID) AS rn FROM TheTable 
) T1 
WHERE rn % 2 = 1 
+0

Mi pensamiento exactamente. – corsiKa

+1

Definitivamente una respuesta viable. ¿Pero esto no raya la distribución de las mitades? En el OP, obtiene "la mitad superior" y "la mitad inferior" en lugar de "dos mitades no calificadas". –

+0

@Mark Canlas: Tiene razón. Mi lectura de la pregunta fue que no importa en qué mitad de cada registro está, siempre que el número de filas sea correcto. Pero tiene razón en que la pregunta podría interpretarse de cualquier manera. –

1

intente esto:

DECLARE @CountOf int,@Top int,@Bottom int 
SELECT @CountOf=COUNT(*) FROM YourTable 
SET @[email protected]/2 
SET @[email protected]@Top 
SELECT TOP (@Top) * FROM YourTable ORDER BY 1 asc --assumes column 1 is your PK 
SELECT TOP (@Bottom) * FROM YourTable ORDER BY 1 desc --assumes column 1 is your PK 
2

Si esto es SQL Server 2000, entonces yo estaría inclinado a encontrar el PK del valor medio de este modo:

Declare @MiddleId int 

Set @MiddleId = (
       Select TOP 1 PK 
       From (
         Select TOP 50 PERCENT PK 
         From Table 
         Order By TheId ASC 
         ) 
       Order By TheId DESC 
       ) 

Select ... 
From Table 
Where TheId <= @MiddleId 

Select .. 
From Table 
Where TheId > @MiddleId 

Con SQL Server 2005, estaría inclinado a hacer lo mismo, pero se puede utilizar un CTE

;With NumProjects As 
    (
    Select Id, ROW_NUMBER() OVER (ORDER BY TheId ASC) As Num 
    From Table 
    ) 
Select @MiddleId = Id 
From Table 
Where Num = CEILING((Select Count(*) From Table)/2) 
0

Esto es the query he encontrado útil (después de las modificaciones de curso):

DECLARE @numberofitemsperpage INT declarar @numberofpages INT DECLARE @currentpage int

DECLARE @countRecords @countRecords flotador SET = (select count (*) A partir de sz_hold_visitsData) - el Excel puede contener aproximadamente un millón de registros a la vez.Si @countRecords> = 1000000 SET @numberofitemsperpage = 500000 else if @countRecords < 1000000 Y @countRecords> = 500000 SET @numberofitemsperpage = 250000 ELSE IF @countRecords < 500000 Y @countRecords> = 100000 SET @numberofitemsperpage = 50000 ELSE SET @numberofitemsperpage = 10000

DECLARE @numberofpages_deci flotador SET @numberofpages_deci = @countRecords/@numberofitemsperpage

@numberofpages SET = techo (@numberofpages_deci) Seleccionar @countRecords AS countRecords, @numberofitemsperpage AS numberofitemsperpage, @numberofpages_deci AS numberofpages_deci, @numberofpages AS numberofpagesFnl

SET @currentpage = 0 mientras @currentpage < @numberofpages COMENZAR SELECCIONAR a. * FROM (SELECT row_number() OVER (ORDER BY person_id) AS ROW, * DE sz_hold_visitsData) una DONDE ROW> = @currentpage * @numberofitemsperpage 1 y la fila < = (@ currentPage + 1) * @numberofitemsperpage

IF @@ ROWCOUNT = 0 ROTURA SET @currentpage = @currentpage 1 END

En este extracto, "sz_hold_visitsData" es una tabla en mi base de datos, mientras que "person_ID" es una columna en la misma. También puede modificar aún más el guión a la salida en el archivo:

DECLARE @numberofitemsperpage INT DECLARE @numberofpages INT DECLARE @currentpage int

@countRecords DECLARE flotan @countRecords SET = (select count (*) De sz_hold_visitsData) - Excel puede contener aproximadamente UN MILLÓN de registros a la vez. Si @countRecords> = 1000000 SET @numberofitemsperpage = 500000 else if @countRecords < 1000000 Y @countRecords> = 500000 SET @numberofitemsperpage = 250000 ELSE IF @countRecords < 500000 Y @countRecords> = 100000 SET @numberofitemsperpage = 50000 ELSE SET @numberofitemsperpage = 10000

DECLARE @numberofpages_deci flotador SET @numberofpages_deci = @countRecords/@numberofitemsperpage

@numberofpages SET = techo (@numberofpages_deci) Seleccionar @countRecords AS countRecords, @numberofitemsperpage AS numberofitemsperpage, @numberofpages_deci AS numberofpages_deci, @numberofpages AS numberofpagesFnl

DECLARE @sevrName nvarchar (50) SET @sevrName ='. \ Sql14' DECLARE nvarchar @outputFile (500)

SET @currentpage = 0 WHILE @currentpage < @numberofpages COMENZAR --Seleccionar a. * FROM (SELECT row_number() OVER (ORDER BY person_id) AS ROW, * FROM sz_hold_visitsData) una DONDE ROW> = @currentpage * @numberofitemsperpage 1 y la fila < = (@ currentpage + 1) * @numberofitemsperpage SET @outputFile = 'C: \ PSM \ outVisits_' + convertir (nvarchar (50), @currentpage) + '.csv '--Seleccionar @outputFile --test

varchar DECLARE @cmd_ (500) = 'sqlcmd -S' + @sevrName +' -E -Q "SELECT a. * FROM (SELECT row_number() OVER (ORDER BY person_ID) AS ROW, * FROM sz_hold_visitsData) WHERE ROW> = '+ CONVERTIR (nvarchar (500), @ currentpage * @numberofitemsperpage +1) +' AND Fila < = '+ CONVERTIR (nvarchar (500) , ((@ actualpágina + 1) * @numberofitemsperpage)) + '"-s", "-o' + @ outputFile + '' - " C: \ PSM \ outVisits.csv "'EXEC xp_cmdshell @cmd_

IF @@ ROWCOUNT = 0 BREAK SET @currentpage = @currentpage +1 END

Hope helps.

+0

Marque su código de la forma prescrita: deje una línea en blanco y sangría cada línea de código con 4 espacios. – STLDeveloper

0

Aquí hay otra solución:

Usted tendría que utilizar una tabla temporal para mantener el primer 50% de la siguiente manera:

select top 50 percent * 
into #YourTempTable 
from TheTable 

-- The below would give the first half 
select * from #YourTempTable 

-- The below woud give rest of the half 
select * from TheTable where TheID not in (select TheID from #YourTempTable) 
Cuestiones relacionadas