How to insert N rows of default values into a table - sql

I have a table containing an identity column as well as a column representing the creation date:
CREATE TABLE dbo.OrderStatus
(
OrderStatusId int IDENTITY(1, 1) NOT NULL,
CreationDate datetime NOT NULL default GETDATE(),
CONSTRAINT PK_OrderStatus PRIMARY KEY(OrderStatusId)
)
Since the identity column generates a value by itself and the CreationDate is always going to be the current date (GETDATE()), I can add a row thanks to DEFAULT VALUES:
INSERT INTO dbo.OrderStatus DEFAULT VALUES;
But what can I do if I want to add, let's say, three records?
Current solution (edited some input since it didn't make any sense)
For now, in order to do what I want, I add several rows with VALUES:
INSERT INTO dbo.OrderStatus (CreationDate)
VALUES (GETDATE()),
(GETDATE()),
(GETDATE())
Although, I'd prefer to know the equivalent of INSERT INTO .. DEFAULT VALUES for multiple rows, in case that I add another column with a default value later on.
Is there a way to insert N rows into a table with DEFAULT VALUES or in a similar way?

An easier way is:
insert dbo.OrderStatus default values
go 500
this will insert 500 rows of default values.

You can use your original definition and just use a while loop, for example
DECLARE #OrderStatus TABLE
(
OrderStatusId int IDENTITY(1, 1) NOT NULL,
CreationDate datetime NOT NULL DEFAULT GETDATE()
--CONSTRAINT PK_OrderStatus PRIMARY KEY(OrderStatusId) -- this can be uncommented if creating a real table.
)
DECLARE #i int = 0;
WHILE #i < 100 -- insert 100 rows. change this value to whatever you want.
BEGIN
INSERT #OrderStatus DEFAULT VALUES
SET #i = #i + 1;
END
SELECT * FROM #OrderStatus
Here's how to do it using a recursive CTE:
;with cteNums(n) AS
(
SELECT 1
UNION ALL
SELECT n + 1
FROM cteNums WHERE n < 100 -- how many times to iterate
)
INSERT #OrderStatus
SELECT * FROM cteNums
Just note that for the CTE you'd have to specify OPTION(MAXRECURSION ...) if it's greater than 100. Also note that even though you're selecting a list of numbers from the CTE, they don't actually get inserted into the table.

The Tally Table method can insert large sets of multiple rows, providing the tally table is big enough. This Tally table will handle up to 1000 entries.
WITH Tally (n) AS
(
-- 1000 rows
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)
)
--SELECT * FROM Tally;
Create Table #temp (id int, d datetime, GUID uniqueidentifier, str1 nvarchar(1), number int)
insert into #temp
select n, getdate(), newid(), 'a', 101 from tally
where N<=100 -- THIS IS WHERE YOU INDICATE HOW MANY ROWS
select * from #temp

Set up a trigger when a new row is CREATEd:
https://msdn.microsoft.com/en-us/library/ms189799.aspx

Related

Delete old entries from table in MSSQL

a table in my MSSQL db is getting over 500MB. I would like to delete the x last entries so the table size is only 100MB. This should be a task which runs once a week. How could I do this?
Example:
Table before deleting the old entries:
Table after deleting the old entries:
You can use DATALENGTH to get the size of the data in a particular column. With a window function, you can sum up a running total of DATALENGTH values. Then you can delete all records in a table that push you past a desired max table size. Here's an example:
-- Sample table with a VARBINARY(MAX) column
CREATE TABLE tmp (id INT IDENTITY(1,1) PRIMARY KEY, col VARBINARY(MAX))
-- Insert sample data - 20 bytes per row.
;WITH cte AS
(
SELECT 1 AS rn, HASHBYTES('sha1', 'somerandomtext') t
UNION all
SELECT rn + 1, HASHBYTES('sha1', 'somerandomtext')
FROM cte
WHERE rn< 5000
)
INSERT INTO tmp (col)
SELECT t FROM cte
OPTION (maxrecursion 0)
-- #total_bytes is the desired size of the table after the delete statement
DECLARE #total_bytes int = 200
-- Use the SUM window function to get a running total of the DATALENGTH
-- of the VARBINARY field, and delete when the running total exceeds
-- the desired table size.
-- You can order the window function however you want to delete rows
-- in the correct sequence.
DELETE t
FROM tmp t
INNER JOIN
(
SELECT id, SUM(DATALENGTH(col)) OVER (ORDER BY id) total_size
FROM tmp
)sq ON t.id = sq.id AND sq.total_size > #total_bytes
Now check what's left in tmp: 10 rows, and the total size of the "col" column matches the 200 byte size specified in the #total_bytes variable.
UPDATE: Here's an example using your sample data:
CREATE TABLE tmp (id INT PRIMARY KEY, contact VARCHAR(100), country VARCHAR(25))
GO
INSERT INTO tmp VALUES
(1, 'Maria Anders', 'Germany'),
(2, 'Francisco Chang', 'Mexico'),
(3, 'Roland Mendel', 'Austria'),
(4, 'Helen Bennett', 'UK'),
(5, 'Yoshi Tannamuri', 'Canada'),
(6, 'Giovanni Rovelli', 'Italy')
GO
-- #total_bytes is the desired size of the table after the delete statement
DECLARE #total_bytes INT = 40
-- Use the SUM window function to get a running total of the DATALENGTH
-- of the VARBINARY field, and delete when the running total exceeds
-- the desired table size.
-- You can order the window function however you want to delete rows
-- in the correct sequence.
DELETE t
FROM tmp t
INNER JOIN
(
SELECT id, SUM(DATALENGTH(contact)) OVER (ORDER BY id)
+ SUM(DATALENGTH(country)) OVER (ORDER BY id) total_size
FROM tmp
)sq ON t.id = sq.id AND sq.total_size > #total_bytes
SELECT * FROM tmp -- 2 rows left!
DELETE FROM TABLE_NAME WHERE date_column < '2018-01-01';
This will delete all data that entered before January 2018
if you want to delete last 7days data
delete from table_name WHERE date_column >= DATEADD(day,-7, GETDATE())

SQL Server : insert data set into table with an identity column

I have a table with an auto-incrementing identity column. Typically I might insert data as follows
INSERT INTO [dbo].[table]
DEFAULT VALUES;
SET #value = SCOPE_IDENTITY();
This way I know the identity value I've just inserted. However I need to insert a "set" of values into that table. Preferably also be able to identify the values I just inserted. I was hoping something similar to the following would be possible ...
INSERT INTO dbo.table DEFAULT VALUES
OUTPUT INSERTED.id INTO #output
SELECT SCOPE_IDENTITY() -- obviously this isn't possible and doesn't actually make sense
FROM #records
WHERE somecolumn IS NULL
I know I might need to set identity_insert on ... I would prefer not to if I don't have to. I am also aware that maybe I could also use some sort of recursive CTE, though I haven't used one of those in a while. Any help would be appreciated.
EDIT: to be clear the question I am asking is: how do I insert a "SET" of data into a table with an auto-incrementing identity column. And hopefully identify the values I just inserted in some way.
INSERT INTO [dbo].[table]
DEFAULT VALUES;
SET #value = SCOPE_IDENTITY();
One does not "typically" do any such thing. It would be highly unusual (to be gentle) to insert a single row that consisted of nothing but default values. And inserting hundreds or thousands of rows is even more suspicious. I think you have chosen a path that doesn't completely make sense.
But let's assume you have not lost your senses. Unfortunately, you cannot insert multiple rows using the "default values" syntax (directly or indirectly). But we can kludge together a script that "sort of" does this (with assumptions) using the output clause suggested by both Gordon and Sachin (using tally table logic here).
set nocount on;
declare #id int;
declare #outputtable table (id int);
create table #x (id int not null identity(1,1), descr varchar(20) null, dd int not null default(2));
insert #x (descr, dd) values ('test', 4), ('zork', 2), (null, 55); -- some extra fluff for demonstartion
insert #x default values;
set #id = SCOPE_IDENTITY();
select #id;
select * from #x order by id;
WITH E00(N) AS (SELECT 1 UNION ALL SELECT 1),
E02(N) AS (SELECT 1 FROM E00 a, E00 b),
E04(N) AS (SELECT 1 FROM E02 a, E02 b),
E08(N) AS (SELECT 1 FROM E04 a, E04 b),
E16(N) AS (SELECT 1 FROM E08 a, E08 b),
E32(N) AS (SELECT 1 FROM E16 a, E16 b),
cteTally(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY N) FROM E32)
insert #x (descr, dd)
output inserted.id into #outputtable(id)
select src.descr, src.dd
from #x as src cross join cteTally
where src.id = #id and cteTally.N < 5;
select x.*
from #outputtable as ids inner join #x as x on ids.id = x.id order by x.id;
if object_id('tempdb..#x') is not null drop table #x;
go
This might not work depending on your table DDL. I'll let you find the assumptions built into this logic.
For an identity column, there is only one way to do this that I am aware of. If you don't mind keeping the dummy around you can skip the alter table statements that add and remove it.
drop table if exists T;
create table T (
id int identity(1, 1) not null
);
alter table T add dummy bit;
insert into T (dummy)
select null
from (
values (42),(555),(911)
) v (v);
alter table T drop column dummy;
select * from T;
You are really close:
INSERT INTO dbo.table
OUTPUT INSERTED.id INTO #output
DEFAULT VALUES;
SELECT *
FROM #output;
The INSERT puts the values into #output. You can then reference them. Remember to define #output as a table variable with a column of the correct type.
Here is a rextester example of it working.
EDIT:
I thought the problem was using #output, because your sample doesn't do that correctly. If your table has a single identity column, then I don't think that SQL Server provides a single-query mechanism for inserting multiple values, unless you turn off identity insert.
One option is a loop:
CREATE TABLE t (id int identity);
DECLARE #output table (id int);
DECLARE #i int = 1;
WHILE #i < 10
BEGIN
INSERT INTO t
OUTPUT INSERTED.id INTO #output
DEFAULT VALUES;
SET #i = #i + 1;
END;
SELECT *
FROM #output;
Another option would be to include another column (even a dummy) just so you can insert something.
And finally, perhaps you don't need a table at all. Perhaps a sequence will suffice for your purposes.
Try this query --
CREATE TABLE StudentPassMarks (ID INT identity(1, 1))
DECLARE #OutputTable TABLE (ID INT)
INSERT INTO StudentPassMarks
OUTPUT inserted.ID
INTO #OutputTable(ID)
DEFAULT VALUES
SELECT * FROM #OutputTable
Go 20;
SELECT * FROM StudentPassMarks

Tally Table in SQL

I want to create a bunch of data with Tally table in SQL (sql2008) and definitely need help.
First of all, I have this table which contains 2 columns.
{
AcctNum (nchar(30), null),
DataInfo (nchar(745), null)
}
While I don't care the data in the DataInfo column, I do want to add about 10k of row into the table with unique AcctNum on each row.
The problem though is I need to keep the length of the data in both column. For example, AcctNum column looks like "400000000000001 ". how do I increment the number while keep the "blank space"?
Not sure if I make much sense here, but please let me know and I will try to explain more, thanks!
Using a recursive common table expression :
-- set up a table variable for demo purpose
declare #t table (AcctNum nchar(30) null, DataInfo nchar(745) null);
-- insert the starting value
insert #t values ('400000000000001', null);
-- run the cte to generate the sequence
with cte (acctnum, num) as (
select acctnum, cast(acctnum as bigint) + 1 num -- starting value
from #t
union all
select acctnum, num+1 from cte
where num < cast(acctnum as bigint) + 10000 -- stopping value
)
-- insert data sequence into the table
insert #t (AcctNum, DataInfo)
select num, null from cte
option (maxrecursion 10000);
select * from #t;
The table variable #t will now contain acctnum 400000000000001 -> 400000000010001 as a contiguous sequence.

setting rowID as identity (should be ascending number) in SQL

I have this following sql script
DECLARE #tmpTable TABLE (rowID int IDENTITY,
woID varchar(100), srID varchar(100),
woWorkOrderNumber varchar(100),
woSequenceCount varchar(100),
WorkOrderNumber varchar(100)
)
INSERT INTO #tmpTable (woID, srID, woWorkOrderNumber, woSequenceCount, WorkOrderNumber)
SELECT
woID, srID, woWorkOrderNumber, woSequenceCount,
SUBSTRING(woWorkOrderNumber, 11, 20 ) AS WorkOrderNumber
FROM
WorkOrder
WHERE
codeSICurrentStatusCode NOT IN (3, 4)
AND SUBSTRING(woWorkOrderNumber, 11, 20) = ''
SELECT * FROM #tmpTable
But I'm getting these results on my rowID column:
As you can see, the rowID seems to be the row number from the table I selected on. What I'm trying to achieve on this temp table is that the rowID starts from 1 then 2 then 3 and so on and so forth.. What's wrong with my code?
You need to specify ORDER BY on the INSERT statement SELECT clause in order to control the order of identity value assignment. You can alternatively use ROW_NUMBER() instead of IDENTITY to ensure there are no gaps and provide complete control over the values.

Need multiple copies of one resultset in sql without using loop

Following is the sample data. I need to make 3 copies of this data in t sql without using loop and return as one resultset. This is sample data not real.
42 South Yorkshire
43 Lancashire
44 Norfolk
Edit: I need multiple copies and I have no idea in advance that how many copies I need I have to decide this on the basis of dates. Date might be 1st jan to 3rd Jan OR 1st jan to 8th Jan.
Thanks.
Don't know about better but this is definatley more creative! you can use a CROSS JOIN.
EDIT: put some code in to generate a date range, you can change the date range, the rows in the #date are your multiplier.
declare #startdate datetime
, #enddate datetime
create table #data1 ([id] int , [name] nvarchar(100))
create table #dates ([date] datetime)
INSERT #data1 SELECT 42, 'South Yorkshire'
INSERT #data1 SELECT 43, 'Lancashire'
INSERT #data1 SELECT 44, 'Norfolk'
set #startdate = '1Jan2010'
set #enddate = '3Jan2010'
WHILE (#startdate <= #enddate)
BEGIN
INSERT #dates SELECT #startdate
set #startdate=#startdate+1
END
SELECT [id] , [name] from #data1 cross join #dates
drop table #data1
drop table #dates
You could always use a CTE to do the dirty work
Replace the WHERE Counter < 4 with the amount of duplicates you need.
CREATE TABLE City (ID INTEGER PRIMARY KEY, Name VARCHAR(32))
INSERT INTO City VALUES (42, 'South Yorkshire')
INSERT INTO City VALUES (43, 'Lancashire')
INSERT INTO City VALUES (44, 'Norfolk')
/*
The CTE duplicates every row from CTE for the amount
specified by Counter
*/
;WITH CityCTE (ID, Name, Counter) AS
(
SELECT c.ID, c.Name, 0 AS Counter
FROM City c
UNION ALL
SELECT c.ID, c.Name, Counter + 1
FROM City c
INNER JOIN CityCTE cte ON cte.ID = c.ID
WHERE Counter < 4
)
SELECT ID, Name
FROM CityCTE
ORDER BY 1, 2
DROP TABLE City
This may not be the most efficient way of doing it, but it should work.
(select ....)
union all
(select ....)
union all
(select ....)
Assume the table is named CountyPopulation:
SELECT * FROM CountyPopulation
UNION ALL
SELECT * FROM CountyPopulation
UNION ALL
SELECT * FROM CountyPopulation
Share and enjoy.
There is no need to use a cursor. The set-based approach would be to use a Calendar table. So first we make our calendar table which need only be done once and be somewhat permanent:
Create Table dbo.Calendar ( Date datetime not null Primary Key Clustered )
GO
; With Numbers As
(
Select ROW_NUMBER() OVER( ORDER BY S1.object_id ) As [Counter]
From sys.columns As s1
Cross Join sys.columns As s2
)
Insert dbo.Calendar([Date])
Select DateAdd(d, [Counter], '19000101')
From Numbers
Where [Counter] <= 100000
GO
I populated it with a 100K dates which goes into 2300. Obviously you can always expand it. Next we generate our test data:
Create Table dbo.Data(Id int not null, [Name] nvarchar(20) not null)
GO
Insert dbo.Data(Id, [Name]) Values(42,'South Yorkshire')
Insert dbo.Data(Id, [Name]) Values(43, 'Lancashire')
Insert dbo.Data(Id, [Name]) Values(44, 'Norfolk')
GO
Now the problem becomes trivial:
Declare #Start datetime
Declare #End datetime
Set #Start = '2010-01-01'
Set #End = '2010-01-03'
Select Dates.[Date], Id, [Name]
From dbo.Data
Cross Join (
Select [Date]
From dbo.Calendar
Where [Date] >= #Start
And [Date] <= #End
) As Dates
By far the best solution is CROSS JOIN. Most natural.
See my answer here: How to retrieve rows multiple times in SQL Server?
If you have a Numbers table lying around, it's even easier. You can DATEDIFF the dates to give you the filter on the Numbers table