Populating data using a Pivot table - sql

I have a table like this,
country 2007 2008 2009
UK 5 10 20
uk 5 10 20
us 10 30 40
us 10 30 40
But I want to populate the table like this,
country year Total volumn
uk 2007 10
uk 2008 20
uk 2009 40
us 2007 20
us 2008 60
us 2009 80
How do I do this in SQL server 2008 using Pivot table or any other method.
http://sqlfiddle.com/#!3/2499a

SELECT country, [year], SUM([Total volumn]) AS [Total volumn]
FROM (
SELECT country, [2007], [2008], [2009]
FROM dbo.test137
) p
UNPIVOT
([Total volumn] FOR [year] IN ([2007], [2008], [2009])
) AS unpvt
GROUP BY country, [year]
ORDER BY country, [year]
See demo on SQLFiddle

Try the following - and remember to change [DB] with your own database name. In my example you do not need to write the years, but they are extracted automatically for you.
-- will contain the temporary total
create table #tempResult (total int)
-- get all countries
declare #countries table (country varchar(50))
insert into #countries
select country from table1
-- get all years
declare #years table(id int identity(1,1), [year] int)
insert into #years
SELECT column_name
FROM [DB].INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = N'Table1'
and column_name != 'country'
-- get all combinations
declare #result table(id int identity(1,1),country varchar(50),[year] int,total int)
insert into #result
select distinct upper(c.country),y.[year],0
from #years as y,#countries as c
-- will be used for the looping
declare #counter int
select #counter = 1
while #counter <= (select count(*) from #result)
begin
declare #year int
declare #country varchar(50)
-- get year and country in question
select #year = [year] from #result where id = #counter
select #country = country from #result where id = #counter
declare #total int
select #total = (select sum(#year) from table1 where country = #country)
-- insert temp result
declare #sql nvarchar(max)
set #sql = N'insert into #tempResult select sum([' + cast(#year as varchar(50)) + ']) from table1 where country = ''' + #country + ''''
print #sql
exec (#sql)
-- extract
select top 1 #total = total from #tempResult
-- update respectively
update #result set total = #total
where country=#country and [year]=#year
-- clear
delete from #tempResult
select #counter = #counter + 1
end
-- select result
select * From #result
drop table #tempResult

Since you are using SQL Server 2008+, then you can use CROSS APPLY to unpivot the data from columns into rows.
You can use the VALUES clause with CROSS APPLY:
select distinct t.country,
c.year,
c.totalvolumn
from yourtable t
cross apply
(
values
('2007', 2007),
('2008', 2008),
('2009', 2009)
) c(year, TotalVolumn)
order by t.country, c.year;
See SQL Fiddle with Demo
Or you can use UNION ALL with CROSS APPLY:
select distinct t.country,
c.year,
c.totalvolumn
from yourtable t
cross apply
(
select '2007', 2007 union all
select '2008', 2008 union all
select '2009', 2009
) c(year, TotalVolumn)
order by t.country, c.year;
See SQL Fiddle with Demo.
This can also be written using a UNION query:
select country, '2007' year, 2007 totalVolumn
from yourtable
union
select country, '2008' year, 2008 totalVolumn
from yourtable
union
select country, '2009' year, 2009 totalVolumn
from yourtable
order by country, year;
See SQL Fiddle with Demo

Related

SQL multiplying rows in select

I would like to select some rows multiple-times, depending on the column's value.
Source table
Article | Count
===============
A | 1
B | 4
C | 2
Wanted result
Article
===============
A
B
B
B
B
C
C
Any hints or samples, please?
You could use:
SELECT m.Article
FROM mytable m
CROSS APPLY (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) AS s(n)
WHERE s.n <= m.[Count];
LiveDemo
Note: CROSS APLLY with any tally table. Here values up to 10.
Related: What is the best way to create and populate a numbers table?
You could also use a recursive CTE which works with numbers > 10 (here up to 1000):
With NumberSequence( Number ) as
(
Select 0 as Number
union all
Select Number + 1
from NumberSequence
where Number BETWEEN 0 AND 1000
)
SELECT Article
FROM ArticleCounts
CROSS APPLY NumberSequence
WHERE Number BETWEEN 1 AND [Count]
ORDER BY Article
Option (MaxRecursion 0)
Demo
A number-table will certainly be the best option.
http://sqlperformance.com/2013/01/t-sql-queries/generate-a-set-2
Please check following SQL script
Before executing the SELECT statement, note that I used a user function which is used to simulate a numbers table
You can find the sql codes of numbers table in SQL Server at referred tutorial
----create table myTempTbl (Article varchar(10), Count int)
--insert into myTempTbl select 'A',1
--insert into myTempTbl select 'B',4
--insert into myTempTbl select 'C',2
select t.*
from myTempTbl t
cross apply dbo.NumbersTable(1,100,1) n
where n.i <= t.Count
order by t.Article
one more CTE
with cte_t as (
select c as c, 1 as i
from mytable
group by c
union all
select t.c, ctet.i + 1
from mytable t
join cte_t ctet
on ctet.c = t.c
and ctet.i < t.i
)
select cte_t.c
from cte_t
order by cte_t.c
Can obtain the output using simple WHILE LOOP
DECLARE #table TABLE
(ID int ,Article varchar(5),[Count] int)
INSERT INTO #table
(ID,Article,Count)
VALUES
(1,'A',1),(2,'B',4),(3,'C',2)
DECLARE #temp TABLE
(Article varchar(5))
DECLARE #Cnt1 INT
DECLARE #Cnt2 INT
DECLARE #Check INT
DECLARE #max INT
SET #max =0
SET #Cnt1 = (SELECT Count(Article) FROM #table)
WHILE (#max < #Cnt1)
BEGIN
SET #max = #max +1
SET #Cnt2 = (SELECT [Count] FROM #table WHERE ID =#max)
SET #Check =(SELECT [Count] FROM #table WHERE ID =#max)
WHILE (#Cnt2 > 0)
BEGIN
INSERT INTO #temp
SELECT Article FROM #table WHERE [Count] =#Check
SET #Cnt2 = #Cnt2 -1
END
END
SELECT * FROM #temp

SQL Query - gather data based on date range - possible variable number of columns

please forgive my inexperience, I hope this isn't too dumb of a question, I'm stuck and have no where else to turn. I'll keep it to the point:
I'm trying to gather payroll data with the results like so:
The issue I have is the variable number of columns. I will be given a date range and are required to return an attendance record for each day in the given range, or a null value if no data is present. I'm using WebAPI as middle tier so I have the ability to perform further data manipulation to achieve this result.
My tables are as follows:
I can't be the first person who needs this done, any articles/posts or anything that would help me accomplish this? Even pseudo code would help; anything!
Thanks a million in advnace!
This is what I've been able to come up with but I'm not even sure if its doable:
-- convert date range into days of month
-- to ensure null values are included in data??
DECLARE #intFlag INT = 0;
DECLARE #numberOfDays INT = DATEDIFF(DAY, #startDate, #endDate);
DECLARE #TMP TABLE (DaysOfMonth date)
WHILE (#intFlag <= #numberOfDays)
BEGIN
INSERT INTO #TMP VALUES (DATEADD(DAY, #intFlag, #startDate));
SET #intFlag = #intFlag + 1
END
-- select days in given data range so c# app can build header row
-- would it help if I pivot this data?
SELECT
DaysOfMonth
FROM
#TMP
ORDER BY
DaysOfMonth
-- get a count for number of people
DECLARE #count INT = 0;
DECLARE #TMPPPL TABLE (Id int identity(1,0), PId Int)
INSERT INTO
#TMPPPL
SELECT
p.PersonId
FROM
dbo.People p
JOIN
dbo.UserTypes ut on p.UserType_UserTypeId = ut.UserTypeId and (ut.Code = 'caregiver' or ut.Code = 'director')
DECLARE #numberOfPeople INT = (SELECT COUNT(1) FROM #TMPPPL)
-- create and execute sproc to return row of data for each person
WHILE (#count <= #numberOfPeople)
BEGIN
-- STUCK HERE, This obviously won't work but what else can I do?
EXEC GetPersonAttendanceHours #personId, #startDate, #endDate;
SET #count = #count + 1
END
This was interesting. I think this will do what you're looking for. First test data:
CREATE TABLE people (PersonID int, Name varchar(30))
INSERT INTO people (PersonID, Name)
SELECT 1, 'Kelly'
UNION ALL SELECT 2, 'Dave'
UNION ALL SELECT 3, 'Mike'
CREATE TABLE attendances (PersonID int, SignIn datetime, SignOut datetime)
INSERT INTO attendances (PersonID, SignIn, SignOut)
SELECT 1, '1-Feb-2015 08:00', '1-Feb-2015 09:00'
UNION ALL SELECT 1, '1-Feb-2015 12:00', '1-Feb-2015 12:30'
UNION ALL SELECT 2, '2-Feb-2015 08:00', '2-Feb-2015 08:15'
UNION ALL SELECT 1, '3-Feb-2015 08:00', '3-Feb-2015 09:00'
UNION ALL SELECT 1, '4-Feb-2015 08:00', '4-Feb-2015 08:30'
UNION ALL SELECT 2, '4-Feb-2015 08:00', '4-Feb-2015 10:00'
UNION ALL SELECT 2, '6-Feb-2015 12:00', '6-Feb-2015 15:00'
UNION ALL SELECT 3, '6-Feb-2015 15:00', '6-Feb-2015 17:00'
UNION ALL SELECT 3, '8-Feb-2015 10:00', '8-Feb-2015 12:00'
Then a dynamic query:
DECLARE #startDate DATETIME='1-Feb-2015'
DECLARE #endDate DATETIME='9-Feb-2015'
DECLARE #numberOfDays INT = DATEDIFF(DAY, #startDate, #endDate)
declare #dayColumns TABLE (delta int, colName varchar(12))
-- Produce 1 row for each day in the report. Note that this is limited by the
-- number of objects in sysobjects (which is about 2000 so it's a high limit)
-- Each row contains a delta date offset, #startDate+delta gives each date to report
-- which is converted to a valid SQL column name in the format colYYYYMMDD
INSERT INTO #dayColumns (delta, colName)
SELECT delta, 'col'+CONVERT(varchar(12),DATEADD(day,delta,#startDate),112) as colName from (
select (ROW_NUMBER() OVER (ORDER BY sysobjects.id))-1 as delta FROM sysobjects
) daysAhead
WHERE delta<=#numberOfDays
-- Create a comma seperated list of columns to report
DECLARE #cols AS NVARCHAR(MAX)= ''
SELECT #cols=CASE WHEN #cols='' THEN #cols ELSE #cols+',' END + colName FROM #dayColumns ORDER BY delta
DECLARE #totalHours AS NVARCHAR(MAX)= ''
SELECT #totalHours=CASE WHEN #totalHours='' THEN '' ELSE #totalHours+' + ' END + 'ISNULL(' + colName +',0)' FROM #dayColumns ORDER BY delta
-- Produce a SQL statement which outputs a variable number of pivoted columns
DECLARE #query AS NVARCHAR(MAX)
SELECT #query=
'declare #days TABLE (reportDay date, colName varchar(12))
INSERT INTO #days (reportDay, colName)
SELECT DATEADD(day,Delta,'''+CONVERT(varchar(22),#startDate,121)+'''), ''col''+CONVERT(varchar(12),DATEADD(day,delta,'''+CONVERT(varchar(22),#startDate,121)+'''),112) as colName from (
select (ROW_NUMBER() OVER (ORDER BY sysobjects.id))-1 as Delta FROM sysobjects
) daysAhead
WHERE Delta<='+CAST(#numberOfDays as varchar(10))+'
SELECT p.Name, pivotedAttendance.*,'+#totalHours+' as totalHours FROM (
SELECT * FROM (
select p.PersonID, d.colName, CAST(DATEDIFF(MINUTE, a.SignIn, a.SignOut)/60.0 as decimal(5,1)) as hrsAttendance
from #days d
CROSS JOIN people p
LEFT OUTER JOIN attendances a ON a.PersonID=p.PersonID AND CAST(a.SignOut as DATE)=d.reportDay
) as s
PIVOT (
SUM(hrsAttendance) FOR colName in ('+#cols+')
) as pa
) as pivotedAttendance
INNER JOIN people p on p.PersonID=pivotedAttendance.PersonID'
-- Run the query
EXEC (#query)
Which produces data in a similar format to your example, with all of the days in the report range and a row for each person. From the above I see:
For presentation purposes you should be able to convert the column name to a display-able date (just parse the YYYYMMDD out of the column name). The date can't be used as the column name directly as it produces an invalid column name.
SQL Fiddle example here.
This is a variation on a theme that I've done in order to display schedules or attendance. I expect something similar should work with your report. Here is the beginning of your stored procedure:
DECLARE #iDay INT = 0;
DECLARE #countDays INT = DATEDIFF(DAY, #startDate, #endDate);
DECLARE #tempDates TABLE ([tempDate] DATE);
DECLARE #filterDates NVARCHAR;
WHILE (#iDay <= #countDays)
BEGIN
INSERT INTO #tempDates VALUES (DATEADD(DAY, #iDay, #startDate));
SET #iDay = #iDay + 1;
END;
SELECT #filterDates = STUFF(
(SELECT N''',''' + CONVERT(NVARCHAR, [tempDate], 103) FROM #tempDates FOR XML PATH('')),
1,
2,
''
);
You were on the right track with your suggestion. The next query gets your data before you PIVOT it.
SELECT [People].[Person_PersonID], [tempDates].[tempDate], [Attendances].[SignIn], [Attendances].[SignOut],
MIN([Attendances].[SignOut], DATEADD(DAY, 1, [tempDates].[tempDate]))
- MAX([Attendances].[SignIn], [tempDates].[tempDate]) * 24 AS [numHours]
FROM [People]
CROSS JOIN #tempDates [tempDates]
LEFT JOIN [Attendances]
ON (
([Attendances].[SignIn] < DATEADD(DAY, 1, [tempDates].[tempDate]))
AND ([Attendances].[SignOut] > [tempDates].[tempDate])
);
Once we're satisfied with the results of the previous query, we substitute it with a query using PIVOT, which should look something like this.
SELECT *
FROM (
SELECT [People].[PersonID], [tempDates].[tempDate], [Attendances].[SignIn], [Attendances].[SignOut],
MIN([Attendances].[SignOut], DATEADD(DAY, 1, [tempDates].[tempDate]))
- MAX([Attendances].[SignIn], [tempDates].[tempDate]) * 24 AS [numHours]
FROM [People]
CROSS JOIN #tempDates [tempDates]
LEFT JOIN [Attendances]
ON (
([Attendances].[SignIn] < DATEADD(DAY, 1, [tempDates].[tempDate]))
AND ([Attendances].[SignOut] > [tempDates].[tempDate])
)
) AS [DatedAttendance]
PIVOT (
SUM([numHours]) FOR ([tempDate] IN (#filterDates))
) AS [PivotAttendance]
ORDER BY [PersonID]

Getting data for a user in a month

(The database is Postgres )
Let's say I have a table races with columns (user_name , race_time , race_speed , race_no).
Each users can have multiple races . I want to get data for each month and show for each month how many races are played in that month. But I also want to show which user played the most and which played has the maximum speed.
select
extract ( month from race_time) as month,
extract ( year from race_time) as year,
count(race_no) as total_races
from races r
group by year,month
order by year desc,month desc
The above query gives me each month and the total races but how can I find for each month which user played the most (that'd be one user)?
I hope someone could help me with this.
SQL Fiddle
with r as (
select
to_char(race_time, 'YYYY-MM') as month,
user_name,
count(*) as n_races,
max(race_speed) as max_speed
from races
group by 1, 2
), total_races as (
select sum(n_races) as total_races, month
from r
group by month
)
select *
from (
select distinct on (month)
month, user_name as user_most_races, n_races as most_races
from r
order by 1, 3 desc
) s
inner join (
select distinct on (month)
month, user_name as user_max_speed, max_speed
from r
order by 1, 3 desc
) q using (month)
inner join
total_races using(month)
order by month
SELECT COUNT(DISTINCT user_name), user_name, EXTRACT(month FROM race_time) AS month, EXTRACT(year FROM race_time) AS year
FROM races r
GROUP BY user_name, year, month
ORDER BY year desc, month desc
Try it!
USE [DATABASE_NAME]
GO
--this is calculating the which user played the most
CREATE FUNCTION [dbo].[GetHighestPlayedRaceUserByYearMonth]
(
-- Add the parameters for the function here
#Year INT, #Month INT
)
RETURNS NVARCHAR(50)
AS
BEGIN
-- Declare the return variable here
DECLARE #UserName NVARCHAR(50)
-- Add the T-SQL statements to compute the return value here
DECLARE #TempTbl TABLE(Played INT,UserName NVARCHAR(50))
INSERT INTO #TempTbl(Played,UserName)
SELECT
COUNT(race_no) AS Played,
[user_name] AS UserName
FROM Table_1
WHERE
YEAR(race_time)=#Year and
MONTH(race_time)=#Month
GROUP BY
MONTH(race_time),
YEAR(race_time),
[user_name]
ORDER BY
YEAR(race_time) DESC,
MONTH(race_time) DESC,
COUNT(race_no) DESC
DECLARE #MaxPlayed INT
SET #MaxPlayed=(SELECT TOP 1 Played FROM #TempTbl ORDER BY Played DESC)
DECLARE #TempTbl2 TABLE(ID INT IDENTITY(1,1) NOT NULL,UserName NVARCHAR(50))
INSERT INTO #TempTbl2
SELECT UserName FROM #TempTbl WHERE Played=#MaxPlayed
DECLARE #ROWCOUNT INT
SET #ROWCOUNT=1
SET #UserName=''
WHILE (SELECT COUNT(*) FROM #TempTbl2)>=#ROWCOUNT
BEGIN
IF (SELECT COUNT(*) FROM #TempTbl2)=#ROWCOUNT
BEGIN
SET #UserName= #UserName+(SELECT UserName FROM #TempTbl2 WHERE ID=#ROWCOUNT)
END
ELSE
BEGIN
SET #UserName= #UserName+(SELECT UserName FROM #TempTbl2 WHERE ID=#ROWCOUNT)+','
END
SET #ROWCOUNT=#ROWCOUNT+1
END
-- Return the result of the function
RETURN #UserName
END
GO
--this is calculating which user played with the maximum speed
CREATE FUNCTION [dbo].[GetMaxRaceSpeedUserByYearMonth]
(
-- Add the parameters for the function here
#Year INT, #Month INT
)
RETURNS NVARCHAR(50)
AS
BEGIN
-- Declare the return variable here
DECLARE #UserName NVARCHAR(50)
-- Add the T-SQL statements to compute the return value here
DECLARE #TempTbl TABLE(MaxRaceSpeed INT,UserName NVARCHAR(50))
INSERT INTO #TempTbl(MaxRaceSpeed,UserName)
SELECT
MAX(race_speed) AS MaxRaceSpeed,
[user_name] AS UserName
FROM Table_1
WHERE
YEAR(race_time)=#Year and
MONTH(race_time)=#Month
GROUP BY
MONTH(race_time),
YEAR(race_time),
[user_name]
ORDER BY
YEAR(race_time) DESC,
MONTH(race_time) DESC,
COUNT(race_no) DESC
DECLARE #MaxRaceSpeed INT
SET #MaxRaceSpeed=(SELECT TOP 1 MaxRaceSpeed FROM #TempTbl ORDER BY MaxRaceSpeed DESC)
DECLARE #TempTbl2 TABLE(ID INT IDENTITY(1,1) NOT NULL,UserName NVARCHAR(50))
INSERT INTO #TempTbl2
SELECT UserName FROM #TempTbl WHERE MaxRaceSpeed=#MaxRaceSpeed
DECLARE #ROWCOUNT INT
SET #ROWCOUNT=1
SET #UserName=''
WHILE (SELECT COUNT(*) FROM #TempTbl2)>=#ROWCOUNT
BEGIN
IF (SELECT COUNT(*) FROM #TempTbl2)=#ROWCOUNT
BEGIN
SET #UserName= #UserName+(SELECT UserName FROM #TempTbl2 WHERE ID=#ROWCOUNT)
END
ELSE
BEGIN
SET #UserName= #UserName+(SELECT UserName FROM #TempTbl2 WHERE ID=#ROWCOUNT)+','
END
SET #ROWCOUNT=#ROWCOUNT+1
END
-- Return the result of the function
RETURN #UserName
END
GO
--Select Query
SELECT
MONTH(race_time) AS [Month],
YEAR(race_time) AS [Year],
COUNT(race_no) AS RacePlayed,
dbo.GetHighestPlayedRaceUserByYearMonth(YEAR(race_time),MONTH(race_time)) AS MaximunPlayedUser,
dbo.GetMaxRaceSpeedUserByYearMonth(YEAR(race_time),MONTH(race_time)) AS MaximunSpeedUser
FROM TABLE_NAME
GROUP BY MONTH(race_time),YEAR(race_time)
ORDER BY YEAR(race_time) DESC,MONTH(race_time) DESC

Show 0 in count SQL

This is my result :
Year matches
2005 1
2008 2
and this is my expected result:
Year matches
2005 1
2006 0
2007 0
2008 2
This is what I have tried:
SELECT DATEPART(yy,A.match_date) AS [Year], COUNT(A.match_id) AS "matches"
FROM match_record A
INNER JOIN match_record B ON A.match_id = B.match_id
WHERE (score) IS NULL OR (score) = 0
GROUP BY DATEPART(yy,A.match_date);
I want to get zero as count in the years where score have some values(not null and zero, anything greater than 0) . Can someone help me?
This might do what you're looking for:
SELECT DATEPART(yy,A.match_date) AS [Year],
SUM(CASE WHEN score=0 or score is null THEN 1 ELSE 0 END) AS "matches"
FROM match_record A
INNER JOIN match_record B ON A.match_id = B.match_id
GROUP BY DATEPART(yy,A.match_date);
Assuming you have any data in the missing years, this should now produce your expected results.
If, instead, you need 0s for years where you have no data, you'll need to provide the list of years separately (say, via a numbers table) and then LEFT JOIN that source to your existing query.
Consider following is your table
SELECT * INTO #TEMP FROM
(
SELECT 2005 [YEARS],1 [MATCHES]
UNION ALL
SELECT 2008,2
)T
Declare two variables to get min and max date in your table
DECLARE #MINYEAR int;
DECLARE #MAXYEAR int;
SELECT #MINYEAR = MIN(YEARS) FROM #TEMP
SELECT #MAXYEAR = MAX(YEARS) FROM #TEMP
Do the following recursion to get years between the period in your table and LEFT JOIN with your table.
; WITH CTE as
(
select #MINYEAR as yr FROM #TEMP
UNION ALL
SELECT YR + 1
FROM CTE
WHERE yr < #MAXYEAR
)
SELECT DISTINCT C.YR,CASE WHEN T.MATCHES IS NULL THEN 0 ELSE T.MATCHES END MATCHES
FROM CTE C
LEFT JOIN #TEMP T ON C.yr=T.YEARS
DECLARE #t table(Year int, matches int)
DECLARE #i int=2005
WHILE #i <=2008
BEGIN
IF NOT exists (SELECT matches FROM tbl WHERE year=#i)
BEGIN
INSERT INTO #t
SELECT #i,'0'
SET #i=#i+1
END
else
BEGIN
INSERT INTO #t
SELECT year,[matches] from tbl
SET #i=#i+1
END
END
SELECT DISTINCT * FROM #t
how about,
SELECT
[year],
COUNT(*) [matches]
FROM (
SELECT
DATEPART(yy, [A].[match_date]) [year]
FROM
[match_record] [A]
LEFT JOIN
[match_record] [B]
ON [A].[match_id] = [B].[match_id]
WHERE
COALESCE([B].[score], 0) = 0) [Nils]
GROUP BY
[Year];

SQL query to find Missing sequence numbers

I have a column named sequence. The data in this column looks like 1, 2, 3, 4, 5, 7, 9, 10, 15.
I need to find the missing sequence numbers from the table. What SQL query will find the missing sequence numbers from my table? I am expecting results like
Missing numbers
---------------
6
8
11
12
13
14
I am using only one table. I tried the query below, but am not getting the results I want.
select de.sequence + 1 as sequence from dataentry as de
left outer join dataentry as de1 on de.sequence + 1 = de1.sequence
where de1.sequence is null order by sequence asc;
How about something like:
select (select isnull(max(val)+1,1) from mydata where val < md.val) as [from],
md.val - 1 as [to]
from mydata md
where md.val != 1 and not exists (
select 1 from mydata md2 where md2.val = md.val - 1)
giving summarised results:
from to
----------- -----------
6 6
8 8
11 14
I know this is a very old post but I wanted to add this solution that I found HERE so that I can find it easier:
WITH Missing (missnum, maxid)
AS
(
SELECT 1 AS missnum, (select max(id) from #TT)
UNION ALL
SELECT missnum + 1, maxid FROM Missing
WHERE missnum < maxid
)
SELECT missnum
FROM Missing
LEFT OUTER JOIN #TT tt on tt.id = Missing.missnum
WHERE tt.id is NULL
OPTION (MAXRECURSION 0);
Try with this:
declare #min int
declare #max int
select #min = min(seq_field), #max = max(seq_field) from [Table]
create table #tmp (Field_No int)
while #min <= #max
begin
if not exists (select * from [Table] where seq_field = #min)
insert into #tmp (Field_No) values (#min)
set #min = #min + 1
end
select * from #tmp
drop table #tmp
The best solutions are those that use a temporary table with the sequence. Assuming you build such a table, LEFT JOIN with NULL check should do the job:
SELECT #sequence.value
FROM #sequence
LEFT JOIN MyTable ON #sequence.value = MyTable.value
WHERE MyTable.value IS NULL
But if you have to repeat this operation often (and more then for 1 sequence in the database), I would create a "static-data" table and have a script to populate it to the MAX(value) of all the tables you need.
SELECT CASE WHEN MAX(column_name) = COUNT(*)
THEN CAST(NULL AS INTEGER)
-- THEN MAX(column_name) + 1 as other option
WHEN MIN(column_name) > 1
THEN 1
WHEN MAX(column_name) <> COUNT(*)
THEN (SELECT MIN(column_name)+1
FROM table_name
WHERE (column_name+ 1)
NOT IN (SELECT column_name FROM table_name))
ELSE NULL END
FROM table_name;
Here is a script to create a stored procedure that returns missing sequential numbers for a given date range.
CREATE PROCEDURE dbo.ddc_RolledBackOrders
-- Add the parameters for the stored procedure here
#StartDate DATETIME ,
#EndDate DATETIME
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Min BIGINT
DECLARE #Max BIGINT
DECLARE #i BIGINT
IF OBJECT_ID('tempdb..#TempTable') IS NOT NULL
BEGIN
DROP TABLE #TempTable
END
CREATE TABLE #TempTable
(
TempOrderNumber BIGINT
)
SELECT #Min = ( SELECT MIN(ordernumber)
FROM dbo.Orders WITH ( NOLOCK )
WHERE OrderDate BETWEEN #StartDate AND #EndDate)
SELECT #Max = ( SELECT MAX(ordernumber)
FROM dbo.Orders WITH ( NOLOCK )
WHERE OrderDate BETWEEN #StartDate AND #EndDate)
SELECT #i = #Min
WHILE #i <= #Max
BEGIN
INSERT INTO #TempTable
SELECT #i
SELECT #i = #i + 1
END
SELECT TempOrderNumber
FROM #TempTable
LEFT JOIN dbo.orders o WITH ( NOLOCK ) ON tempordernumber = o.OrderNumber
WHERE o.OrderNumber IS NULL
END
GO
Aren't all given solutions way too complex?
wouldn't this be much simpler:
SELECT *
FROM (SELECT row_number() over(order by number) as N from master..spt_values) t
where N not in (select 1 as sequence union
select 2 union
select 3 union
select 4 union
select 5 union
select 7 union
select 10 union
select 15
)
This is my interpretation of this issue, placing the contents in a Table variable that I can easily access in the remainder of my script.
DECLARE #IDS TABLE (row int, ID int)
INSERT INTO #IDS
select ROW_NUMBER() OVER (ORDER BY x.[Referred_ID]), x.[Referred_ID] FROM
(SELECT b.[Referred_ID] + 1 [Referred_ID]
FROM [catalog].[dbo].[Referrals] b) as x
LEFT JOIN [catalog].[dbo].[Referrals] a ON x.[Referred_ID] = a.[Referred_ID]
WHERE a.[Referred_ID] IS NULL
select * from #IDS
Just for fun, I decided to post my solution.
I had an identity column in my table and I wanted to find missing invoice numbers.
I reviewed all the examples I could find but they were not elegant enough.
CREATE VIEW EENSkippedInvoicveNo
AS
SELECT CASE WHEN MSCNT = 1 THEN CAST(MSFIRST AS VARCHAR (8)) ELSE
CAST(MSFIRST AS VARCHAR (8)) + ' - ' + CAST(MSlAST AS VARCHAR (8)) END AS MISSING,
MSCNT, INV_DT FROM (
select invNo+1 as Msfirst, inv_no -1 as Mslast, inv_no - invno -1 as msCnt, dbo.fmtdt(Inv_dt) AS INV_dT
from (select inv_no as invNo, a4glidentity + 1 as a4glid
from oehdrhst_sql where inv_dt > 20140401) as s
inner Join oehdrhst_sql as h
on a4glid = a4glidentity
where inv_no - invno <> 1
) AS SS
DECLARE #MaxID INT = (SELECT MAX(timerecordid) FROM dbo.TimeRecord)
SELECT SeqID AS MissingSeqID
FROM (SELECT ROW_NUMBER() OVER (ORDER BY column_id) SeqID from sys.columns) LkUp
LEFT JOIN dbo.TimeRecord t ON t.timeRecordId = LkUp.SeqID
WHERE t.timeRecordId is null and SeqID < #MaxID
I found this answer here:
http://sql-developers.blogspot.com/2012/10/how-to-find-missing-identitysequence.html
I was looking for a solution and found many answers. This is the one I used and it worked very well. I hope this helps anyone looking for a similar answer.
-- This will return better Results
-- ----------------------------------
;With CTERange
As (
select (select isnull(max(ArchiveID)+1,1) from tblArchives where ArchiveID < md.ArchiveID) as [from],
md.ArchiveID - 1 as [to]
from tblArchives md
where md.ArchiveID != 1 and not exists (
select 1 from tblArchives md2 where md2.ArchiveID = md.ArchiveID - 1)
) SELECT [from], [to], ([to]-[from])+1 [total missing]
From CTERange
ORDER BY ([to]-[from])+1 DESC;
from to total missing
------- ------- --------------
6 6 1
8 8 1
11 14 4
DECLARE #TempSujith TABLE
(MissingId int)
Declare #Id Int
DECLARE #mycur CURSOR
SET #mycur = CURSOR FOR Select Id From tbl_Table
OPEN #mycur
FETCH NEXT FROM #mycur INTO #Id
Declare #index int
Set #index = 1
WHILE ##FETCH_STATUS = 0
BEGIN
if (#index < #Id)
begin
while #index < #Id
begin
insert into #TempSujith values (#index)
set #index = #index + 1
end
end
set #index = #index + 1
FETCH NEXT FROM #mycur INTO #Id
END
Select Id from tbl_Table
select MissingId from #TempSujith
Create a useful Tally table:
-- can go up to 4 million or 2^22
select top 100000 identity(int, 1, 1) Id
into Tally
from master..spt_values
cross join master..spt_values
Index it, or make that single column as PK.
Then use EXCEPT to get your missing number.
select Id from Tally where Id <= (select max(Id) from TestTable)
except
select Id from TestTable
You could also solve using something like a CTE to generate the full sequence:
create table #tmp(sequence int)
insert into #tmp(sequence) values (1)
insert into #tmp(sequence) values (2)
insert into #tmp(sequence) values (3)
insert into #tmp(sequence) values (5)
insert into #tmp(sequence) values (6)
insert into #tmp(sequence) values (8)
insert into #tmp(sequence) values (10)
insert into #tmp(sequence) values (11)
insert into #tmp(sequence) values (14)
DECLARE #max INT
SELECT #max = max(sequence) from #tmp;
with full_sequence
(
Sequence
)
as
(
SELECT 1 Sequence
UNION ALL
SELECT Sequence + 1
FROM full_sequence
WHERE Sequence < #max
)
SELECT
full_sequence.sequence
FROM
full_sequence
LEFT JOIN
#tmp
ON
full_sequence.sequence = #tmp.sequence
WHERE
#tmp.sequence IS NULL
Hmmmm - the formatting is not working on here for some reason? Can anyone see the problem?
i had made a proc so you can send the table name and the key and the result is a list of missing numbers from the given table
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
create PROCEDURE [dbo].[action_FindMissing_Autoincremnt]
(
#tblname as nvarchar(50),
#tblKey as nvarchar(50)
)
AS
BEGIN
SET NOCOUNT ON;
declare #qry nvarchar(4000)
set #qry = 'declare #min int '
set #qry = #qry + 'declare #max int '
set #qry = #qry +'select #min = min(' + #tblKey + ')'
set #qry = #qry + ', #max = max('+ #tblKey +') '
set #qry = #qry + ' from '+ #tblname
set #qry = #qry + ' create table #tmp (Field_No int)
while #min <= #max
begin
if not exists (select * from '+ #tblname +' where '+ #tblKey +' = #min)
insert into #tmp (Field_No) values (#min)
set #min = #min + 1
end
select * from #tmp order by Field_No
drop table #tmp '
exec sp_executesql #qry
END
GO
SELECT TOP 1 (Id + 1)
FROM CustomerNumberGenerator
WHERE (Id + 1) NOT IN ( SELECT Id FROM CustomerNumberGenerator )
Working on a customer number generator for my company. Not the most efficient but definitely most readable
The table has one Id column.
The table allows for Ids to be inserted at manually by a user off sequence.
The solution solves the case where the user decided to pick a high number