Selecting a Range of Numbers while joining - sql

I'm selecting a range of numbers 1-6 with a CTE. I have another table that has two columns. One with any number between 1-6 and a column called code.
I want to always return 1-6 but join on my other table that has the code.
An Example would be:
Table 1
Number
------
1
2
3
4
5
6
Table 2
Number | Code
------ -----
1 B
3 A
5 C
I want the Select to return:
CodeNumber | Code
------ -----
1 B
2 NULL
3 A
4 NULL
5 C
6 NULL
If I join on the number it doesn't return all the values.
DECLARE #Start INT
DECLARE #End INT
DECLARE #Priority CHAR(2)
SELECT #Start = -3
, #End = 3
, #Code = ''
WITH Numbers (Number, Code)
AS ( SELECT #Start AS Number, #Code
UNION ALL
SELECT Number + 1, #Code
FROM Numbers
WHERE Number < #End
)

Use a left join:
WITH Numbers(Number) as (
SELECT #Start AS Number
UNION ALL
SELECT Number + 1
FROM Numbers
WHERE Number < #End
)
select n.number, t.code
from numbers n left join
table2 t
on t.number = n.number;

Related

The way for insert and fill table base on column in another table in sql with Several million rows

I have a table similar A With 2 million recordes
ROW ID ITEM NoOfUnit
1 1 A 2
2 2 B 1
3 3 C 3
.
.
.
I want fill table B base on NoOfUnit from A Similar to the below
ROW ID ITEM QTY
1 1 A 1
2 1 A 1
3 2 B 1
4 3 C 1
5 3 C 1
6 3 C 1
.
.
.
Number of rows in table B very large and cursor very slow...
I would just use a recursive CTE:
with cte as (
select id, item, NoOfUnit, 1 as n
from a
union all
select id, item, NoOfUnit, n + 1
from a
where n < NoOfUnit
)
insert into b (id, item, qty)
select id, item, 1
from cte;
If qty is ever greater than 100, then you need option (maxrecursion 0).
All you need to do here is duplicate your rows based on the number held in NoOfUnit, which you could do with a numbers table. You then insert the result of this into your destination table.
An example of how to do this is as follows:
Query
declare #d table(ID int, ITEM char(1),NoOfUnit int);
insert into #d values
(1,'A',2)
,(2,'B',1)
,(3,'C',3)
;
with t as(select t from(values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) as t(t)) -- table with 10 rows
,n as(select row_number() over (order by (select null)) as n from t,t t2,t t3,t t4,t t5) -- cross join 10 rows 5 times for 10 * 10 * 10 * 10 * 10 = 100,000 rows with incrementing value
select d.ID
,d.ITEM
,1 as QTY
from #d as d
join n
on d.NoOfUnit >= n.n
order by d.ID
,d.ITEM;
Output
ID
ITEM
QTY
1
A
1
1
A
1
2
B
1
3
C
1
3
C
1
3
C
1

How to use #tmp Table in Loop in Recursive Function

I am trying to create a loop that when given a part id, it will search a table of assembly parts and explode all the parts into a large list.
It needs to be recursive because Part 123 may have parts 1, 2, 3, 4, 5 and parts 4 and 5 are also assembly items.
I thought I had come up with something pretty good and easily returns the part id and the part level for each item. Then I find out that I can't use temp tables, so it shoots my loop down.
What can I use in place of the temp table to give me the same function here?
CREATE FUNCTION [dbo].[fn_getParts] (
#source_part_id int
, #level int
)
RETURNS #parts_list TABLE (
[part] int NOT NULL,
[level] int NOT NULL
)
AS
BEGIN
DECLARE
#max int = 0,
#cnt int = 0,
#PID int = 0,
#Plvl int = 0,
#id int = 0
INSERT INTO #parts_list VALUES (#source_part_id, #level)
SET #level += 1
SELECT [Comp_Part_ID] AS [PID], #level AS [level]
INTO #chkParts
FROM [assemblies]
WHERE [Assy_PID] = #source_part_id
SELECT #max = COUNT(*) FROM #chkParts
WHILE #cnt <= #max
BEGIN
SELECT #PID = [PID], #Plvl = [level] FROM #chkParts
INSERT INTO #parts_list
SELECT * FROM [fn_getParts](#PID, #Plvl)
SET #cnt += 1
END
RETURN
END
Here's some sample data:
CREATE TABLE [Assemblies] (
[PartID] int,
[Comp_PartID] int
)
INSERT INTO [Assemblies] VALUES
(1,2),
(1,3),
(1,4),
(1,5),
(1,6),
(3,9),
(3,10),
(10,11),
(10,23),
(10,24),
(10,31),
(11,24),
(11,23)
If I enter SELECT * FROM [fn_getParts](1,0) I would expect the following:
part,level
1,0
2,1
3,1
4,1
9,2
10,2
11,3
23,3
24,3
The code can be simplified somewhat by wrapping an Inline Table-Valued Function around a Recursive CTE, e.g.:
create function dbo.fn_getParts (
#source_part_id int
)
returns table as return (
with PartsHierarchy as (
select #source_part_id as part, 0 as level
union all
select Comp_PartID, 1 + level
from Assemblies
join PartsHierarchy on part = PartID
)
select part, level
from PartsHierarchy
);
And then, invoking it for different part numbers...
select * from dbo.fn_getParts(1);
part level
---- ----
1 0
2 1
3 1
4 1
5 1
6 1
9 2
10 2
11 3
23 3
24 3
31 3
24 4
23 4
select * from dbo.fn_getParts(10);
part level
---- -----
10 0
11 1
23 1
24 1
31 1
24 2
23 2
select * from dbo.fn_getParts(11);
part level
---- -----
11 0
24 1
23 1

T-SQL Query to get ending position in a result set

I have a result set to which I will provide an input position and a number for iteration. I expect a result of the end position.
-------------
ID
-------------
1
2
3
4
5
6
7
--------------------------------
InputPosition Iteration
--------------------------------
4 6
----------------
ID Iteration
----------------
1 5
2 6 (EndPosition = 2)
3
4 1
5 2
6 3
7 4
Hence I need to get the 'EndPosition' for this scenario.
I'm not sure how important the tables are to the task at hand. If the answer is not very, the following may work for you:
declare #input as int = 4
declare #itemCount as int = 7
declare #iterations as int = 6
select
case when (#input + #iterations - 1) % #itemCount = 0 then 7
else (#input + #iterations - 1) % #itemCount
end as EndPosition
If the tables are important, then you may be able to use this logic in combination with the row_number() function.
This will work only for your sequential number set.
declare #table table (id int)
insert into #table (id)
values
(1),
(2),
(3),
(4),
(5),
(6),
(7)
declare #inputPosition int = 4
declare #iteration int = 6
;with cte as(
select
id,
row_number() over (order by case when id = #inputPosition then 1 else #inputPosition +1 end) as rn
from #table)
select
*
from
cte
where rn = #iteration

For different groups, sql query to replace null value with known value from next known value

I have this SQL table which looks like this:
customer date number
--------- ---- ------
A 1 3
A 2 NULL
A 3 5
A 4 NULL
A 5 6
B 1 NULL
B 2 NULL
B 3 10
Per customer, I'm looking to add an extra column number_NEW which replaces the NULL in number (if this is null) with the next known chronologically known number (determined by date):
customer date number number_NEW
--------- ---- ------ ----------
A 1 3 3
A 2 NULL 5
A 3 5 5
A 4 NULL 6
A 5 6 6
B 1 NULL 10
B 2 NULL 10
B 3 10 10
How would I go about this in SQL?
Thanks a lot!
You can use APPLY:
SELECT
*,
Number_NEW = ISNULL(t.Number, x.Number)
FROM Test t
OUTER APPLY(
SELECT TOP 1 Number
FROM Test
WHERE
Customer = t.Customer
AND Date > t.Date
AND Number IS NOT NULL
ORDER BY Date
)x
ORDER BY t.Customer, t.Date
Your sample data is not upto the mark .
[date] column is not clear.So to be safe I have use row_number which I think is require.
Also I think your problem is already solved.I have written this script using sql 2012 with dynamic LEAD().
It not only giving correct output but also depict dynamic use of LEAD().
Declare #t table(customer varchar(20),[date] int, number int)
insert into #t values
('A', 1,3 )
,('A', 2, NULL)
,('A', 3, 5 )
,('A', 4, NULL )
,('A', 5, 6)
,('B', 1, NULL)
,('B', 2, NULL)
,('B', 3, 10)
;WITH CTE
AS (
SELECT *
,ROW_NUMBER() OVER (
PARTITION BY customer ORDER BY [DATE]
) RN
FROM #T
)
--SELECT * FROM CTE
SELECT *
,IIF(number IS NULL, LEAD(number, (
SELECT TOP 1 RN - A.RN
FROM CTE
WHERE customer = a.customer
AND RN > a.RN
AND number IS NOT NULL
ORDER BY RN
), number) OVER (
ORDER BY customer
,[date]
), number) number_NEW
FROM CTE A
alter table T add number_NEW int null;
update T /* substitute table name here -- I realize that SQL Server allows aliases */
set number_NEW =
case
when number is null
then (
select min(t2.number) /* do date and number always increase together? */
from T as t2
/* substitute full table name here as well */
where t2.customer = T.customer and t2.date > T."date"
)
else number
end
);
alter table T alter column number_NEW int not null;

SQL select step

I need to fetch a number of rows between two column values, using a defined step value. For example, if the table looks like this:
Id Name
-----------------------
1 Maria Anders
2 Christina Berglund
3 Francisco Chang
4 Roland Mendel
5 Diego Roel
6 Eduardo Saavedra
7 Helen Bennett
8 Philip Cramer
and First = 3, Last = 7, Step = 2, query should return:
Id Name
-----------------------
3 Francisco Chang
5 Diego Roel
7 Helen Bennett
I was thinking of using a modulo to specify which columns should be returned, with something like:
SELECT *
FROM Table
WHERE (i-3) % 2 = 0
This approach will result in SQL Server iterating through the entire table and calculating the expression for each item. Since I expect to have relatively large step values, I would like to know if there is a strategy which would avoid this (possible using an index to "skip" items).
Is there a better (read: faster) way of doing this? (I am using MS SQL Server 2008 R2)
DECLARE #start int = 3, #step int = 2, #stop int = 7;
;WITH cte AS
(
SELECT #start AS ID
UNION ALL
SELECT ID + #step FROM cte WHERE ID + #step <= #stop
)
SELECT *
FROM cte JOIN MyTable M ON cte.ID = MyTable.ID
select * from table
where (id >= #start)
AND (id<=#end)
AND ((id-#start)%#step) = 0
Test case:
declare #start int =3,
#end int = 7,
#step int =2
;with t(id)
as
(
select 1
union select 2
union select 3
union select 4
union select 5
union select 6
union select 7
union select 8
)
select * from t
where (id >= #start) AND (id<=#end) and ((id-#start)%#step) = 0
output:
3
5
7