SQL Server make table as a function parameter - sql

I have this function, and I want to make the table '[SEJ_Reservation]' as a parameter to the function, to be able to use the function with another table, the function as below :
CREATE FUNCTION [dbo].[fn_Inhouse]()
RETURNS #TEM_INHOUSE TABLE (LogID int ,InHouseDate DATE)
AS
BEGIN
with
l0 as (select 0 v from (values(0),(0),(0),(0),(0),(0),(0),(0),(0),(0))v(v)),
l1 as (select 0 v from l0 a cross join l0 b),
nums as (select n = Row_Number() over(order by ##Spid) from l1)
insert into #TEM_INHOUSE (LogID, InHouseDate)
select ro.LogID, DateAdd(day, -n, ro.C_OUT) as InHouseDate
from [dbo].[SEJ_Reservation] ro
join nums n on n.n <= ro.Gun;
RETURN;
END;
I try something like that :
CREATE TYPE TableType
AS TABLE (LogID int,C_OUT datetime,Gun int)
GO
CREATE FUNCTION [dbo].[fn_Inhouse5](#mytable TableType READONLY)
RETURNS #TEM_INHOUSE TABLE (LogID int ,InHouseDate DATE)
AS
BEGIN
with
l0 as (select 0 v from (values(0),(0),(0),(0),(0),(0),(0),(0),(0),(0))v(v)),
l1 as (select 0 v from l0 a cross join l0 b),
nums as (select n = Row_Number() over(order by ##Spid) from l1)
insert into #TEM_INHOUSE (LogID, InHouseDate)
select ro.LogID, DateAdd(day, -n, ro.C_OUT) as InHouseDate
from #mytable ro
join nums n on n.n <= ro.Gun;
RETURN
END;
GO
SELECT * from dbo.[fn_Inhouse5]('[SEJ_Reservation]')
but got message error:
Operand type clash: varchar is incompatible with TableType

You need to declare the table type, insert rows, and pass it as a table-valued parameter for the function. T-SQL example:
DECLARE #TableType TableType;
INSERT INTO #TableType (LogID, C_OUT, Gun)
SELECT LogID, C_OUT, Gun
FROM SEJ_Reservation;
SELECT * from dbo.[fn_Inhouse5](#TableType);
In .NET application code, you can pass the TVP rows as a DataReader, DataTable, or IEnumerable<SqlDataRecord>.

Related

Generating Data Using CTE

I need to generate 500 records using CTE, my output table should be something like below
TEST
DESCRIPTION
ID
TEST1
DESC1
1
TEST2
DESC2
2
TEST3
DESC3
3
TEST4
DESC4
4
I struggling with how to create it with cte as it has to be wrapped inside view
i created temp table its working fine but i wasnt able to wrap it inside view
i tried the following code
with cte1 as (
CREATE TABLE #code1(TEST,DESCRIPTION)
declare #TEST int
set #TEST = 1
While #TEST <= 500
Begin
Insert Into #code1(TEST,DESCRIPTION)
values ('TEST' + CAST(#TEST as varchar(100)),'DESC' + CAST(#TEST as varchar(100)))
Print #TEST
Set #TEST = #TEST + 1
End
)
I think I am doing it in the wrong way, can anyone give me any suggestions here.
Your INSERT...SELECT syntax is all off. Start by creating the table normally, then INSERT the rows.
The best way to generate a lot of rows is to use a tally function. Itzik Ben-Gan's cross-join is great, here is a simplified version of it, good for up to 65,536 rows
WITH
L0 AS ( SELECT 1 AS c
FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),
(1),(1),(1),(1),(1),(1),(1),(1)) AS D(c) ),
L1 AS ( SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B ),
L2 AS ( SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B ),
Nums AS ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum
FROM L2 )
INSERT #code1 (TEST, DESCRIPTION, ID)
SELECT TOP(500)
CONCAT('TEST', rownum) AS TEST,
CONCAT('DESC', rownum) AS DESCRIPTION,
rownum AS ID
FROM Nums;
You can create a stored procedure to achieve a similar result:
SP Code:
DELIMITER $$
CREATE PROCEDURE Proc00()
BEGIN
CREATE TABLE code1 (TEST VARCHAR(20),DESCRIPTION VARCHAR(30),ID INT);
SET #TEST = 1;
WHILE #TEST <= 500 DO
INSERT INTO code1
VALUES (CONCAT('TEST',#TEST),CONCAT('DESC',#TEST),#TEST);
SET #TEST = #TEST + 1;
END WHILE;
SELECT * FROM code1;
END $$
DELIMITER ;
Then let's execute the stored procedure by using CALL() statement:
CALL Proc00();
It returns:
...............

Workaround for temporary Table in view SQL Server

I have the following problem: I want to create a view that contains among others a table, whose column 'verylongtext' should be split in strings that mustn't exceed 50 characters.
this result set should be joined in the view.
the temporary table would be created the following way:
create table #results(id int, string varchar(400))
declare #results table(id int, string varchar(400))
declare #id int
declare #strings varchar(400)
set #id = 0
while exists (select * from roottable where row_id > #id)
begin
select top 1 #id = row_id, #strings = verylongtext from roottable
where row_id > #id
order by row_id asc
insert into #results
select #id, data from dbo.Split( [dbo].[DelineateEachNth](#strings, 50, '$'), '$')
end
The problem is of course, that no temporary tables are allowed in views. CTEs don't seem to work with the resultset of the function. Is there any possible other way? I am absolutely clueless. Thanks in advance!!
You can call your split function directly on the base table using APPLY, meaning no temp table or loops are required:
SELECT r.id, s.data
FROM RootTable AS r
CROSS APPLY dbo.Split(dbo.DelineateEachNth(r.VeryLongText, 50, '$'), '$') AS s;
You may find the scalar function dbo.DelineateEachNth is a performance killer (as all scalar UDFs are), as such an alternative way to split the string is to use a tally table:
CREATE FUNCTION dbo.FixedLengthSplit
(
#String NVARCHAR(MAX),
#Length INT
)
RETURNS TABLE
WITH SCHEMABINDING AS
RETURN
( WITH N1 AS (SELECT N FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1), (1)) n (N)),
N2(N) AS (SELECT 1 FROM N1 a CROSS JOIN N1 b),
N3(N) AS (SELECT 1 FROM N2 a CROSS JOIN N2 b),
N4(N) AS (SELECT 1 FROM N3 a CROSS JOIN N3 b),
Numbers (N) AS
( SELECT TOP (CONVERT(INT, CEILING(1.0 * ISNULL(LEN(#String),1) / #Length)))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1
FROM n4
)
SELECT ItemNumber = N + 1,
Data = SUBSTRING(#String, 1 + (#Length * N), #Length)
FROM Numbers
);
Then your view is just:
SELECT *
FROM rootTable AS r
CROSS APPLY dbo.FixedLengthSplit(r.VeryLongString, 50) AS s;

Inserting individual values into table based on a number

Here is my problem: I have a stored procedure in SQL Server 2012 which should do the following thing.
I will pass an input parameter #Range, and the stored procedure should insert values into a table starting from 0 to #Range-1.
CREATE PROC MyExample
(#Range INT)
AS
BEGIN
// Suppose the value of #Range is 100
// So I should do INSERT into MyTable Values(0,1,2,3,4,5,6,......99)
END
Any idea how to achieve this?
You can use while loop as below:
Declare #Index AS INT=0
WHILE #Index<#Range
BEGIN
INSERT into MyTable Values(#Index)
SET #Index=#Index+1
END
I am thinking your teacher may suspect why you use cte when you just learn a loop
CREATE PROC MyExample
(
#Range INT,
)
AS
BEGIN
;WITH numbers AS
(
SELECT 0 AS Value WHERE #Range >= 0 -- Validate the #Range value too, try 0 or negative values
UNION ALL SELECT Value + 1 FROM numbers WHERE Value + 1 < #Range
)
INSERT INTO MyTable
SELECT * FROM numbers
OPTION (MAXRECURSION 0)
END
And here is a set based approach:
CREATE PROC MyExample
(
#Range INT,
)
AS
BEGIN
INSERT INTO MyTable (Number)
SELECT TOP (#Range) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) - 1
FROM sys.objects s1
CROSS JOIN sys.objects s2
END
(Based on this SO post)
CREATE PROC MyExample
(
#Range INT,
)
AS
BEGIN
declare #RANGE_COUNT int
select #RANGE_COUNT =#Range
//Suppose the value of #Range is 100
while #RANGE_COUNT<>0
begin
//So I should do INSERT into MyTable Values(0,1,2,3,4,5,6,......99)
INSERT into MyTable Values(#Range)
set #RANGE_COUNT = RANGE_COUNT -1
end
END
Using tally table technique:
DECLARE #range INT = 100
SELECT TOP(#range) -1 + ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rn
FROM
(VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t1(n) CROSS JOIN --10
(VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t2(n) CROSS JOIN --100
(VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t3(n) --1000
--...continue to cover all possible #range values

Fastest way to insert 100000 records into SQL Server

I am using the following script to insert 100,000 records into a table. Basically int from 500,001 to 600,000 are inserted. I am casting the integer into a string and inserting coz thats how i want it in the table (an integer in the form of string). I am using a merge to check if the record already exists or not.
DECLARE #first AS INT
SET #first = 500001
DECLARE #step AS INT
SET #step = 1
DECLARE #last AS INT
SET #last = 600000
BEGIN TRANSACTION
WHILE(#first <= #last)
BEGIN
MERGE dbo.Identifiers As target
USING (SELECT CAST(#first as varchar(10)) AS Identifier) AS source
ON (source.Identifier = target.Identifier)
WHEN NOT MATCHED THEN
INSERT (Identifier)
VALUES (source.Identifier);
SET #first += #step
END
COMMIT TRANSACTION
Its taking more than 2 minutes to load. I am doing something terribly wrong but unable to trace out where.
Note: The table has unique non-clustered index on Identifier Column.
I am wondering how much your procedural looping and the MERGE (instead of a simple INSERT) contributes to bad performance. I would opt for a strictly set-based solution like this:
INSERT INTO dbo.Identifiers (Identifier)
SELECT n FROM dbo.GetNums(500001, 600000)
WHERE n NOT IN (SELECT Identifier FROM dbo.Identifiers);
Now, this relies on a user-defined table-valued function dbo.GetNums that returns a table containing all numbers between 500,001 and 600,000 in a column called n. How do you write that function? You need to generate a range of numbers on the fly inside it.
The following implementation is taken from the book "Microsoft SQL Server 2012 High-Performance T-SQL Using Window Functions" by Itzik Ben-Gak.
CREATE FUNCTION dbo.GetNums(#low AS BIGINT, #high AS BIGINT) RETURNS TABLE
AS
RETURN
WITH L0 AS (SELECT c FROM (VALUES(1),(1)) AS D(c)),
L1 AS (SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B),
L2 AS (SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B),
L3 AS (SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B),
L4 AS (SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B),
L5 AS (SELECT 1 AS c FROM L4 AS A CROSS JOIN L4 AS B),
Nums AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS rownum FROM L5)
SELECT #low + rownum - 1 AS n
FROM Nums
ORDER BY rownum
OFFSET 0 ROWS FETCH FIRST #high - #low + 1 ROWS ONLY;
(Since this comes from a book on SQL Server 2012, it might not work on SQL Server 2008 out-of-the-box, but it should be possible to adapt.)
Try this one. It uses a tally table. Reference: http://www.sqlservercentral.com/articles/T-SQL/62867/
create table #temp_table(
N int
)
declare #first as int
set #first = 500001
declare #step as int
set #step = 1
declare #last as int
set #last = 600000
with
e1 as(select 1 as N union all select 1), --2 rows
e2 as(select 1 as N from e1 as a, e1 as b), --4 rows
e3 as(select 1 as N from e2 as a, e2 as b), --16 rows
e4 as(select 1 as N from e3 as a, e3 as b), --256 rows
e5 as(select 1 as N from e4 as a, e4 as b), --65,356 rows
e6 as(select 1 as N from e5 as a, e1 as b), -- 131,072 rows
tally as (select 500000 + (row_number() over(order by N) * #step) as N from e6) -- change 500000 with desired start
insert into #temp_table
select cast(N as varchar(10))
from tally t
where
N >= #first
and N <=#last
and not exists(
select 1 from #temp_table where N = t.N
)
drop table #temp_table
Vinoth, Something given below could also help you.
Declare #tab table (id int identity(1,1),num int)
Insert into #tab (num) Values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
Declare #start as int
set #start = 500000
Insert into dbo.Identifiers (Identifier)
Select #start + ((E.id-1)*10000) +((D.id-1)*1000) +((C.id-1)*100) + ((B.id-1) * 10) + A.id
from #tab A,#tab B,#tab C,#tab D,#tab E
Order by #start + ((E.id-1)*10000) +((D.id-1)*1000) +((C.id-1)*100) + ((B.id-1) * 10) + A.id
In my DB, dbo.Identifiers is a table without any index. It took only 230 ms for the insertion.
Create index on the Identifier column and then try the above insert

finding the missing values in a Sequence

Table1 is as follows :
Col1
1
2
3
4
6
7
8
9
10
13
14
As shown above the col1 has the sequence of values but for some reason the user did not insert 5, 11 and so on. How to find out the missing values in a sequence. Here the sequence is 1 to 14 and the missing values are 5,11. Please help me.
As was said in other answers, the best choice is to do a join with a real sequence table. You can create one using a recursive CTE:
DECLARE #MaxNumber INT
SELECT #MaxNumber = MAX(Col1) FROM YourTable;
WITH CTE AS
(
SELECT 1 Col1
UNION ALL
SELECT Col1+1
FROM CTE
WHERE Col1+1 <= #MaxNumber
)
SELECT A.Col1
FROM CTE A
LEFT JOIN YourTable B
ON A.Col1 = B.Col1
WHERE B.Col1 IS NULL
OPTION(MAXRECURSION 0)
This will work for numbers 0 - 2000 for large numbers you just need to cross join the original result set.
with temp as (
select distinct number
from master..spt_Values
where number between 0 and 2000
)
select * from
temp t
left join your_table y on y.col1 = t.number
where y.col1 is null
alternatively using cross join
This will work for billions obviously slower
WITH
L0 AS(SELECT 1 AS c UNION ALL SELECT 1),
L1 AS(SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B),
L2 AS(SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B),
L3 AS(SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B),
L4 AS(SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B),
L5 AS(SELECT 1 AS c FROM L4 AS A CROSS JOIN L4 AS B),
Nums AS(SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS n FROM L5)
select * from
l5 t
left join your_table y on y.col1 = t.n
where y.col1 is null
This seems to pretty much be a duplication of
SQL query to find Missing sequence numbers
There's a suggestions this will work:
SELECT l.id + 1 as start
FROM Table1 as l
LEFT JOIN Table1 as r on l.id + 1 = r.id
WHERE r.id IS NULL
Otherwise you can left join on your table with a sequence table. From the above question, you can look at http://www.projectdmx.com/tsql/tblnumbers.aspx to get some ideas on how to generate a suitable sequence table, and the join will be something like
SELECT #sequence.value
FROM #sequence
LEFT JOIN Table1 ON #sequence.value = Table1.value
WHERE Table1.value IS NULL
Side-note to all recursive CTE suggestions. The recursive CTE increases time linear to the number of rows. Using a tally table or cross-join is much better to use...
This would work:
-- data table
CREATE TABLE #data (
value INT
)
INSERT #data VALUES (1)
INSERT #data VALUES (2)
INSERT #data VALUES (3)
INSERT #data VALUES (4)
INSERT #data VALUES (6)
INSERT #data VALUES (7)
INSERT #data VALUES (8)
INSERT #data VALUES (9)
INSERT #data VALUES (10)
INSERT #data VALUES (13)
INSERT #data VALUES (14)
-- normally i have a tally table already for stuff like this but I'll
-- create one temporary here.
CREATE TABLE #tmp_tally (
n INT
)
DECLARE #n INT
SET #n = 1
WHILE #n < 14
BEGIN
INSERT #tmp_tally VALUES (#n)
SET #n = #n + 1
END
SELECT
T.n,
CASE WHEN #data.value IS NULL THEN 'Missing' ELSE 'Not Missing' END
FROM
#tmp_tally T
LEFT JOIN #data ON
T.n = #data.value
WHERE
T.n <= (SELECT MAX(value) FROM #data) -- max of what you want to check against which is 14 in your example
DROP TABLE #data
DROP TABLE #tmp_tally
Try this:
declare #min int
declare #max int
select #min = min(field_ID), #max = max(field_ID) from [Table]
create table #tmp (Field_No int)
while #min <= #max
begin
if not exists (select * from [Table] where field_ID = #min)
insert into #tmp (seq_field) values (#min)
set #min = #min + 1
end
select * from #tmp
drop table #tmp
With the above script you will get missing values in "ID" column from #tmp table.
Hope this will help you!!
I would do a subquery in the same table, to see if another number exist for the current number-1, and if there is not one, you know that a number was skipped. You can do the +1 of this as well.
select
nt.numb,
CASE
(select COUNT(*) from table where numb=nt.numb-1)=0 THEN 'skipped' ELSE 'not skipped'
from
numbertable nt