2009-05-06 5 views
2

in SQL Server zurückzugeben, wenn ich eine int-Variable habe, wie kann ich eine Abfrage schreiben, die die Werte 1 durch den Wert der int-Variablen zurückgibt?SQL-Abfrage, um 1 bis int Variable

So gegeben:

declare @cnt int 
set @cnt = 5 

Welche Abfrage 1 zurückkehren würde - 5? Ist das möglich?

Antwort

5

Eine alternative Antwort auf die anderen bereits gegeben, vielleicht nicht so praktisch, aber es kann in bestimmten Situationen schneller sein.

Wenn Sie eine obere Schranke bekannt für die Werte von @cnt Sie (etwa 1000) trifft man auf eine einfache Ein-Spalten-Tabelle mit den ersten 1000 Zahlen in es schaffen könnte, wie

 
val 
--- 
1 
2 
3 
4 
... 

und dann, wenn Sie eine Menge von ganzen Zahlen wollen könnte man es

 
select val 
from integerTable 
where val <= @cnt 
+0

Klingt verrückt, aber Tabellen von Zahlen speichern Schleife viel Zeit, die oft effizienter ist. – u07ch

+0

Dies ist der Gewinner. Die Daten, die ich durchlaufe, haben Millionen von Zeilen, und ich muss diese Art von Operation pro Zeile ausführen. Dies ist eine sehr gute Lösung – Turbo

4

Sie könnten eine WÄHREND Schleife in SQL wie folgt tun:

DECLARE @Cnt int, 
     @Val int; 
SET @Cnt = 5; 
SET @Val = 0; 

DECLARE @MyValues TABLE 
(
    val int 
); 

WHILE @Val < @Cnt 
BEGIN 
    SET @Val = @Val + 1; 
    INSERT @MyValues(val) 
    SELECT @Val; 
END; 
SELECT Val FROM @MyValues; 
+0

Ja, ich habe gehofft, dass nicht jede Art oder Cursor-basierte Verarbeitung (Zeile Cursor oder während zu tun Schleife). Ich habe auf eine Set-basierte Ausführungslösung gehofft. – Turbo

+0

vielleicht könnten Sie in eine Funktion einbinden, würde es einfacher zu bedienen – codeulike

0
declare @cnt int 
    set @cnt = 0 
    while @cnt < 5 
    begin 
     set @cnt = @cnt + 1 
     print 'The counter is ' + cast(@counter as char) 
    end 
1

Sie können einen „Ausgang“ Wert nur am Ende Ihrer Abfrage eine SELECT-Anweisung leicht implementieren ...

SELECT ... FROM ... WHERE ... Bla Bla, yadah, yadah; 
SELECT (1 - @cnt); 

und Sie werden die Ausgabe haben, die Sie wollen.

+0

Ich glaube nicht, er meinte '1 minus @cnt', er meinte 'alle Ganzzahlen von 1 bis @cnt in einer Tabelle' – codeulike

+0

ohhh ... I hab diesen Teil nicht bekommen! Ich dachte wirklich, es wäre 'minus', weil programmatisch 1 bis 5 du schreibst [1 ... 5] :) – balexandre

2

Sie können es in einer Abfrage mit einem recursive CTE tun, aber Sie müssen den MAXRECURSION Abfragehinweis beachten, wenn Ihr @cnt mit einem sehr hohen Wert beginnt.

DECLARE @cnt int; 
SET @cnt = 5; 

WITH Numbers AS (
    SELECT @cnt [Value] 

    UNION ALL 

    SELECT [Value] - 1 
    FROM Numbers 
    WHERE [Value] > 1 
) 
SELECT * FROM Numbers 
ORDER BY [Value] 

Trotzdem gesagt, wäre es einfacher und leichter zu lesen, es mit einer einfachen WHILE-Schleife zu tun.

+0

Große Antwort. Ich wusste, dass jemand Rekursion denken würde. (Vergessen Sie nicht das ';' vor 'WITH'.) –

+0

Ja - ich vergesse immer ein Semikolon nach dem Befehl unmittelbar vor dem WITH hinzuzufügen. Ich werde die Variablendeklaration des OPs als sehr explizit hinzufügen. –

+0

Standardmäßig funktioniert dies nicht mehr um @ cnt = 100, da die maximale Rekursionsgrenze erschöpft ist. – Andomar

0

wusste nicht, ob Sie nur eine resultset wollte (wie pro José Basilio's answer) oder waren mit mehr als einem

DECLARE @ctr INT 
DECLARE @start INT 

SET @ctr = 5 
SET @start = 1 

WHILE @start <= @ctr 
BEGIN 
    SELECT @start 
    SET @start = @start + 1 
END 
1

Hier ist eine Funktion, das zu tun:

CREATE FUNCTION [dbo].[Sequence] (@start int, @end int, @step int) 
RETURNS @result table (num int) AS 
BEGIN 
    declare @num int 
    set @num = @start 

    while @num <= @end 
     begin 
     insert into @result (num) values (@num) 
     set @num = @num + @step 
     end 
    return 
END 

Mit der Funktion , können Sie Sequenzen wie wählen:

select * from dbo.Sequence(1,5,1) 

Dies ergibt 1,2,3,4,5.

select * from dbo.Sequence(2,6,2) 

Wird 2,4,6 zurückgeben. Und so weiter :)

1

Wenn der Maximalwert von @cnt in Ihrem Kontext relativ niedrig erhalten verwendet, ist, eine temporäre oder persistente Tabelle ganzer Zahlen, und wählen Sie aus oder registriere dich jetzt bevölkern könnten es.

CREATE TABLE LotsOfNumbers(Number int not null) 
INSERT INTO LotsOfNumbers VALUES(1) 
INSERT INTO LotsOfNumbers VALUES(2) 
INSERT INTO LotsOfNumbers VALUES(3) 
... 
INSERT INTO LotsOfNumbers VALUES(99) 
INSERT INTO LotsOfNumbers VALUES(100) 


DECLARE @cnt INT 
SET @cnt = 30 

SELECT * 
FROM LotsOfNumbers 
WHERE Number <= @cnt 
ORDER BY Number 

Natürlich ist die Zeit/Raum-Leistung, das wird von Ihrer Situation abhängen, also würde ich SQL Profiler sehe für jede dieser Methoden eine Vorstellung von dem Hit auf der DB zu bekommen.

0

Erstellen einer Tabelle von einzelnen Spalten mit Zahlen ist einfach.

Create Table Numbers(Number INT); 

INSERT INTO Numbers(Number) values(0); 
INSERT INTO Numbers(Number) values(1); 
INSERT INTO Numbers(Number) values(2); 
. 
. 
INSERT INTO Numbers(Number) values(9); 

-- To generate a list of 100000 

SELECT * INTO TEST 
FROM 
(SELECT N1.Number * 1000 + N2.Number * 100 + N3.Number * 10 + N4.Number * 1 as Num 
FROM Numbers N1 
CROSS JOIN Numbers N2 
CROSS JOIN Numbers N3 
CROSS JOIN Numbers N4) AS N 
ORDER BY Num; 

--Then, You can use the above table for getting the numbers 
SELECT * from TEST WHERE Num <= 5; 
0

Ich hatte mit vor Jahren beschäftigen, so dachte ich, die schnellste/einfachste Art und Weise heraus, ob Ihre Datenbank eine Tabelle mit mehreren Datensätzen hat, als Sie zählen müssen.

Erstens, trotz der Vorschläge müssen Sie nicht eine Tabelle von Datensätzen "erstellen" ... alles, was Sie brauchen, ist "ANY" -Tabelle, die bereits Datensätze hat. Solange die Tabelle mehr Datensätze enthält, als Sie zählen müssen, sind Sie gut.

SELECT top (5) ROW_NUMBER() OVER(Order by [Primary_Key] desc) AS Integer 
FROM SOME_TABLE --that has more than 5 rows. 

AUCH dies ist die elegante (Mathe) Art und Weise. Wo Sie keine große Tabelle bereits in der Datenbank haben.

Verwenden Sie die Split-Rekursion, mit der Sie eine Tabelle mit bis zu 2^n-1 ganzen Zahlen erzeugen können, wobei n die Rekursionsgrenze ist. Plus dies ist vollständig parametriert, so dass es leicht in Funktion gesetzt werden kann

DECLARE @n int, @cnt int; 
set @cnt = 5; 
SET @n = LOG(@cnt)/LOG(2) +1; --use change of base to find LOG base 2 

WITH Num AS (
    SELECT @n [Value] 
    UNION ALL 

    SELECT [Value] - 1 
    FROM Num 
    WHERE [Value] > 1 

    UNION ALL 

    SELECT [Value] - 1 
    FROM Num 
    WHERE [Value] > 1 
) 

SELECT * from (
SELECT ROW_NUMBER() OVER(Order by Value asc) AS [Integer] 
FROM Num 
)i 
where [Integer] <= @cnt 

Der einzige Nachteil ist, dass es doppelt so viele Datensätze erzeugen kann wie während Rekursion benötigt, so hat es das Potenzial für mehr Overhead als Inline-Rekursion. ABER vermeidet dadurch vollständig die Rekursionsgrenze. (Sie sollten nicht immer eine Notwendigkeit, die ersten 2^100-1 ganze Zahlen aufzulisten.) (Auch vorhersagen, ich werde einfach Sie mit so viel Festplattenspeicher keinen Computer haben.)

Diese Nachteil ist, warum ich die nächste Funktion erstellt habe. Dieselbe Doubling-Technik wie die Split-Rekursion, aber innerhalb einer While-Schleife. UND das letzte Mal, es läuft nur die notwendigen Datensätze nicht doppelt. Mit dieser Funktion können Sie auch einen Bereich angeben und alle Zahlen in diesem Bereich mit einem 0-basierten Index dieser Zahlen zurückgeben. Ich habe diese Null-Basis-Spalte als nutzloses Artefakt der Berechnung betrachtet, aber es hat sich als äußerst hilfreich erwiesen, sowohl den Bereich als auch den Index in einer Zeile zu haben.

create function [dbo].[fn_Integers] 
(
@Min_Integer int = 1 
,@Max_Integer int 
) 
RETURNS @retTable TABLE 
    ([BASE] int identity(0,1) 
    ,[INTEGER] int) 
AS 
BEGIN 
    Declare @RANGE int 
    SET @RANGE = @[email protected]_Integer 

    insert into @retTable 
    select NULL --initial value for recursion 

    While ((SELECT MAX([BASE]) FROM @retTable r) < @RANGE) 
    BEGIN 
     INSERT into @retTable 
     select [INTEGER] from @retTable r 
     where [BASE] < (SELECT @RANGE - MAX([BASE]) FROM @retTable r) 
    END 

    Update @retTable set 
    [INTEGER] = [BASE] + @Min_Integer 

RETURN 
END 
GO 

kann es etwa 50000 Zahlen pro Sekunde tun (auf meinem Arbeitsplatz) und Ausgabe sieht wie folgt

SELECT * FROM [fn_Integers](18,50000) 

BASE INTEGER 
----------------- 
0  18 
1  19 
2  20 
... 
49982 50000