SQL create table with integers - sql

I want to have in a database a table with single column with integers (1,2,...) since it can be helpful for certain joins.
I came up with a solution using a loop. Is there a more efficient way of creating such a table?
My solution
CREATE TABLE #NUM
(
NUM int
)
DECLARE #i int=1
WHILE #i<10000
BEGIN
INSERT INTO #Temp
SELECT #i
SET #i = #i + 1
END

I will do this using a tally table
;WITH e1(n) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), -- 10
e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b), -- 10*10
e3(n) AS (SELECT 1 FROM e2 CROSS JOIN e2 AS b) -- 100*100
INSERT INTO #Temp
SELECT ROW_NUMBER() OVER (ORDER BY n) FROM e3 ORDER BY n;
check here for more info

SELECT
row_number() over (order by message_id) num
into #Table
from sys.messages

This is the same principle as the answer by NoDisplayName, but I think it is a little cleaner (my opinion):
;WITH TBL(ROW_NUM) AS (
SELECT 1 AS ROW_NUM
UNION ALL
SELECT ROW_NUM+1
FROM TBL
WHERE ROW_NUM < 100
)
SELECT ROW_NUMBER() OVER (ORDER BY T1.ROW_NUM) AS ROW_NUM
FROM TBL T1
JOIN TBL T2 ON 1=1
JOIN TBL T3 ON 1=1

Related

Can I delete multiple of nth rows in a single query from a table in SQL?

I want to delete multiple of 4 from my table which have thousands of record. How can I do it?
Ex:-
Table1
1 a
2 b
3 c
4 d
5 e
6 f
7 g
8 h
9 i
I want to delete every 4th row.
I don't want to use a loop or cursor.
Delete A from
(
Select *,Row_Number() Over(Order By Id) as RN from TableA
) A
where RN%4=0
SQL Fiddle Link
Try this...
delete from table_name where (col1 % 4) = 0
Use a CTE.
WITH cte AS (
SELECT t.*, ROW_NUMBER() OVER (ORDER BY t.rowfield) AS rank
FROM Table1 t)
SELECT rowfield, fielda
FROM cte
WHERE rank%4 != 0
Output
rowfield fielda
1 a
2 b
3 c
5 e
6 f
7 g
9 i
SQL Fiddle: http://sqlfiddle.com/#!6/c9540b/13/0
Once you are happy with the output use DELETE FROM.
This can use an index if one exists and uses numbers table
;with cte
as
(select n from numbers
where n%4=0
)
delete t
from table1 t
join
cte c
on c.n=t.id
Try this
DECLARE #nvalToDelete varchar(100)='4,7,3,8,9'-- just give values to delete from table
DECLARE #temp TABLE
(
valtodelete VARCHAR(100)
)
DECLARE #Deletetemp TABLE
(
valtodelete INT
)
INSERT INTO #temp
SELECT #nvalToDelete
INSERT INTO #Deletetemp
SELECT split.a.value('.', 'VARCHAR(1000)') AS valToDelete
FROM (SELECT Cast('<S>' + Replace(valtodelete, ',', '</S><S>')
+ '</S>' AS XML) AS valToDelete
FROM #temp) AS A
CROSS apply valtodelete.nodes('/S') AS Split(a)
DECLARE #Table1 TABLE
(
ID INT,
val varchar(10)
)
INSERT INTO #Table1
SELECT 1,'a' UNION ALL
SELECT 2,'b' UNION ALL
SELECT 3,'c' UNION ALL
SELECT 4,'d' UNION ALL
SELECT 5,'e' UNION ALL
SELECT 6,'f' UNION ALL
SELECT 7,'g' UNION ALL
SELECT 8,'h' UNION ALL
SELECT 9,'i'
SELECT *
FROM #Table1;
WITH cte
AS (SELECT *,
RN = Row_number()
OVER (
ORDER BY id )
FROM #Table1)
DELETE FROM #Table1
WHERE id IN(SELECT id FROM cte
WHERE rn IN (SELECT CASt(valToDelete AS INT) FROM #Deletetemp)
)
SELECT *
FROM #Table1

Returning while-loop values as multiple rows in SQL Server instead of multiple result sets

I have the following T-SQL, used to generate some random values:
DECLARE #cnt INT = 0;
WHILE #cnt < 100
BEGIN
select
Random_String =
substring(x,(abs(checksum(newid()))%36)+1,1)+
substring(x,(abs(checksum(newid()))%36)+1,1)+
substring(x,(abs(checksum(newid()))%36)+1,1)
from
(select x='0123456789ABCDEFGHJKLMNPQRSTUWXYZ%#-=+') a
SET #cnt = #cnt + 1;
END;
This works well enough, except every string is returned as, what looks like, an independent result set.
Is there a way to refactor that query to return every value as one row in the same result set?
Environment is MS SQL Server 2008.
Thanks!
The best way to do this kind of thing is to forget about looping in t-sql. Using a numbers or tally table is a much better way to go about this.
WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a cross join E1 b), --10E+2 or 100 rows
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E2
)
select
Random_String =
substring(x,(abs(checksum(newid()))%36)+1,1)+
substring(x,(abs(checksum(newid()))%36)+1,1)+
substring(x,(abs(checksum(newid()))%36)+1,1)
from
(select x='0123456789ABCDEFGHJKLMNPQRSTUWXYZ%#-=+') a
cross join cteTally t
where t.N < = 100
Here's a way to do it using a tally table:
;With Tally (N) As
(
Select 0 Union All
Select 1 Union All
Select 2 Union All
Select 3 Union All
Select 4 Union All
Select 5 Union All
Select 6 Union All
Select 7 Union All
Select 8 Union All
Select 9
), Numbers (N) As
(
Select Row_Number() Over (Order By A.N) Num
From Tally A -- 10
Cross Join Tally B -- 100
Cross Join Tally C -- 1000
Cross Join Tally D -- 10000
Cross Join Tally E -- 100000
), LookupString (X) As
(
Select '0123456789ABCDEFGHJKLMNPQRSTUWXYZ%#-=+'
)
Select Random_String = substring(x,(abs(checksum(newid()))%36)+1,1)+
substring(x,(abs(checksum(newid()))%36)+1,1)+
substring(x,(abs(checksum(newid()))%36)+1,1)
From LookupString
Cross Join Numbers
Where N <= 100

Split row into several with SQL statement

I have a row in a databasetable that is on the following form:
ID | Amount | From | To
5 | 5439 | 01.01.2014 | 05.01.2014
I want to split this up to one row pr month using SQL/T-SQL:
Amount | From
5439 | 01.01.2014
5439 | 02.01.2014
5439 | 03.01.2014
5439 | 04.01.2014
5439 | 05.01.2014
I, sadly, cannot change the database source, and I want to preferrably do this in SQL as I am trying to result of this Query with an other table in Powerpivot.
Edit: Upon requests on my code, I have tried the following:
declare #counter int
set #counter = 0
WHILE #counter < 6
begin
set #counter = #counter +1
select amount, DATEADD(month, #counter, [From]) as Dato
FROM [database].[dbo].[table]
end
This however returns several databasesets.
You can use a tally table to generate all dates.
SQL Fiddle
;WITH E1(N) AS(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b),
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b),
Tally(N) AS(
SELECT TOP(SELECT MAX(DATEDIFF(DAY, [From], [To])) + 1 FROM yourTable)
ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM E4
)
SELECT
yt.Id,
yt.Amount,
[From] = DATEADD(DAY, N-1, yt.[From])
FROM yourTable yt
CROSS JOIN Tally t
WHERE
DATEADD(DAY, N-1, yt.[From]) <= yt.[To]
Simplified explanation on Tally Table
You need a tally table with "running numbers". This may be a function (I posted one shortly here: https://stackoverflow.com/a/32096945/5089204) or a physical table (I posted an example here: https://stackoverflow.com/a/32474751/5089204) or a CTE to do this "on the fly" (the table example does it this way).
If you go with the posted function it could be like this:
declare #startDate DATETIME={d'2015-09-01'};
declare #EndDate DATETIME={d'2015-09-10'};
select DATEADD(DAY, Nmbr,#startDate)
from dbo.GetRunningNumbers(DATEDIFF(DAY,#startDate,#endDate)+1,0);
select * INTO #TEMP1 from
(values
(5 , 5439 , '01.01.2014', '05.01.2014'))t(id,amount,fromd,tod)
WITH CTE
AS
(
SELECT CAST(FROMD AS DATE) AS FROMD,amount,1 AS RN,ID FROM #TEMP1
UNION ALL
SELECT DATEADD(M,1,C.FROMD),C.amount,C.RN+1,C.ID
FROM CTE C
INNER JOIN #TEMP1 T ON T.id = C.ID AND DATEADD(M,1,c.FROMD)<=T.tod
)
SELECT * FROM CTE
create table t (fd date, td date)
insert into t values ('2015-01-01','2015-01-05')
WITH DATES (fd, td, Level)
AS
(
SELECT fd, td, 0 AS Level
FROM t
UNION ALL
-- Recursive member definition
SELECT DATEADD(day,level+1,e.fd),e.td,Level + 1
FROM t AS e
INNER JOIN Dates AS d ON DATEADD(day,-d.level,d.fd) = e.fd AND d.fd < d.td
)
-- Statement that executes the CTE
SELECT fd,td,level
from DATES
variant using recursive cte
--variable table for data sample
DECLARE #tbl AS TABLE
(
ID INT ,
Amount FLOAT ,
[From] DATE ,
[To] DATE
)
INSERT INTO #tbl
( ID, Amount, [From], [To] )
VALUES ( 5, 5439, '2014-01-01', '2014-01-05' )
--final query using recursive cte
;
WITH cte
AS ( SELECT T.ID ,
T.Amount ,
T.[From] ,
T.[To] ,
CONVERT(DATE, NULL) AS Dt ,
n = 0
FROM #tbl AS T
UNION ALL
SELECT cte.ID ,
cte.Amount ,
cte.[From] ,
cte.[To] ,
DATEADD(DAY, n, cte.[From]) ,
cte.n + 1
FROM cte
WHERE n <= DATEDIFF(day, cte.[From], cte.[To])
)
SELECT cte.ID ,
cte.Amount ,
dt AS [From]
FROM cte
WHERE cte.Dt IS NOT NULL
SQL Fiddle

SQL Server SQL Get list of unused ids

I have a table that ranges from 1-100000 but there are gaps in the ids where items have been deleted. I want a SQL statement that will return me a list of all unused ids in the table so I can get a list of items that were deleted.
I want the list but randomizing the list is a bonus actually. I think it can be done with a rand function...
I'd like to keep it ansi SQL if possible to maintain portability but if not, then that's ok...
You can do this with a use of Tally Table.
Create our sample data.
CREATE TABLE #ids(
id INT IDENTITY(1, 1)
)
SET IDENTITY_INSERT #ids ON
--Insert 100,000 rows
INSERT INTO #ids(id)
SELECT TOP 100000 ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM sys.columns a
CROSS JOIN sys.columns b
SET IDENTITY_INSERT #ids OFF;
-- Randomly delete 1000 rows
WITH cte AS(
SELECT TOP 1000 id
FROM #ids
ORDER BY NEWID()
)
DELETE FROM cte
Using a Tally Table, create a list of numbers from 1 - 100,000. Then use NOT EXISTS to get the unused ids. To randomize the list, add on ORDER BY NEWID() clause.
DECLARE #min INT = 1,
#max INT = 100000
;WITH E1(N) AS(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b),
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b),
E8(N) AS(SELECT 1 FROM E4 a CROSS JOIN E4 b),
Tally(N) AS(
SELECT TOP(#max) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM E8
)
SELECT N
FROM Tally t
WHERE NOT EXISTS(
SELECT 1 FROM #ids WHERE id = t.N
)
ORDER BY NEWID() -- Sorts the result in a random order

How to select parents and children rows in SQL Server

I want to select all itens related to user_id 53 (parents and children) from the following table. It should be: 1, 2, 4, 8, 9.
my_table
--------------------------------------------
id parent_id user_id sequence depth
--------------------------------------------
1 null 50 1 1
2 1 52 1.2 2
3 1 52 1.3 2
4 2 53 1.2.4 3
5 2 52 1.2.5 3
6 3 52 1.3.6 3
7 3 51 1.3.7 3
8 4 51 1.2.4.8 4
9 4 51 1.2.4.9 4
With CTE I could select all children or parents, but I could'nt select children and parents with just one query. Below is the cte I'm using to select children.
Item and children
with cte as (
select t.id, t.parent_id, t.user_id
from my_table t
where t.user_id=53
union all
select t.id, t.parent_id, t.user_id
from my_table t
inner join cte c on (c.parent_id=t.id)
)
select t.* from cte t;
Item and parents
with cte as (
select t.id, t.parent_id, t.user_id
from my_table t
where t.user_id=53
union all
select t.id, t.parent_id, t.user_id
from my_table t
inner join cte c on (c.id=t.parent_id)
)
select t.* from cte t;
Thanks.
It is very convenient that you have the sequences. The parents have a sequence matching an initial subset of the one you are looking for. The same is true for the children, but in reverse.
The following comes close to what you want:
select mt.*
from (select sequence from my_table where USER_ID = 53) theone join
my_table mt
on mt.sequence like theone.sequence+ '%' or
theone.sequence like mt.sequence + '%'
However, you have to be careful with 10 matching 1, for instance. So, let's add an additional period where appropriate:
select mt.*
from (select sequence from my_table where USER_ID = 53) theone join
my_table mt
on mt.sequence like theone.sequence+ '.%' or
theone.sequence like mt.sequence + '.%'
The problem here is, that you want a recursive SELECT. A DBMS is not made for such a query. But it's of course possible.
SELECT t1.* FROM Table1 t1 WHERE user_id = 53
UNION ALL
SELECT t2.* FROM Table1 t1, Table1 t2
WHERE
(t1.id = t2.parent_id OR t1.parent_id = t2.id) AND t1.user_id = 53
This query would give you each 1st degree relative.
For a recursive SELECT, have a look here: Recursive select in SQL
Solution for your query here: http://sqlfiddle.com/#!6/e1542/10/0
with cte as (
select t.id, t.parent_id, t.user_id
from Table1 t
where t.user_id=53
),
cte1 as (
select t.id, t.parent_id, t.user_id
from cte t
union all
select t.id, t.parent_id, t.user_id
from Table1 t
inner join cte1 c on (c.parent_id=t.id)
),
cte2 as (
select t.id, t.parent_id, t.user_id
from cte t
union all
select t.id, t.parent_id, t.user_id
from Table1 t
inner join cte2 c on (c.id=t.parent_id)
)
select t.* from cte1 t
UNION
select t.* from cte2 t
declare #Rownumber int;
CREATE TABLE #GetSeqUser (
RowNumber int,
UserId int,
)
INSERT INTO #GetSeqUser Select ROW_NUMBER() OVER(ORDER BY EmpId ASC) AS ROWNUMBER, EmpId FROM dbo.TreeSystem
set #Rownumber = (select ROWNUMBER from #GetSeqUser where UserId = 3 )
set #Rownumber = #Rownumber + 1
select UserId from #GetSeqUser where ROWNUMBER = #Rownumber
declare #Rownumber int;
CREATE TABLE #GetSeqUser (
RowNumber int,
UserId int,
)
INSERT INTO #GetSeqUser Select ROW_NUMBER() OVER(ORDER BY EmpId ASC) AS ROWNUMBER, EmpId FROM dbo.TreeSystem
set #Rownumber = (select ROWNUMBER from #GetSeqUser where UserId = 3 )
set #Rownumber = #Rownumber + 1
select UserId from #GetSeqUser where ROWNUMBER = #Rownumber