tags:

views:

100

answers:

3

I have to divide the rows equally, so here, for example, there are 15 rows. I want to divide equally, which is in three groups, but I want the name to come only in front of the first entry of each group, as shown:

DECLARE @NAMES TABLE
(
[ID] INT IDENTITY,
[NAME] VARCHAR(20)
)


INSERT INTO @NAMES
SELECT 'NAME1' UNION ALL
SELECT 'NAME2' UNION ALL
SELECT 'NAME3' UNION ALL
SELECT 'NAME4' UNION ALL
SELECT 'NAME5' UNION ALL
SELECT 'NAME6' UNION ALL
SELECT 'NAME7' UNION ALL
SELECT 'NAME8' UNION ALL
SELECT 'NAME9' UNION ALL
SELECT 'NAME10' UNION ALL
SELECT 'NAME11' UNION ALL
SELECT 'NAME12' UNION ALL
SELECT 'NAME13' UNION ALL
SELECT 'NAME14' UNION ALL
SELECT 'NAME15'

Desired Output:

ID          NAME
----------- --------------------
1           NAME1
2           
3           
4           
5           
6           NAME6
7           
8           
9           
10          
11          NAME11
12          
13          
14    
15
A: 
SELECT ID, CASE WHEN (ID = 1 OR ID = 6 OR ID = 11) THEN Name Else NULL END
FROM @Names

I know it is a stupid way of doing it.
But, it is much better to write it in this way for a limited set of rows (as per your example).

If you have more number of rows (than 15), please post.
I will try to see if I can derive a formula so that the results can be printed per your expectations.

shahkalpesh
+6  A: 

If you are using SQL 2005 or above, the following should do the job for any number of rows:

declare @numBuckets;
select @numBuckets = 3;

;with nameBase as
(
 select ntile(@numBuckets) over(order by ID) as bucket,
   NAME, ID
 from @NAMES
),
nameRows as
(
 select row_number() over(partition by bucket order by ID) as rn,
   NAME, ID
 from nameBase

)
select n.ID, case when rn = 1 then n.NAME else null end as NAME
from nameRows n
order by ID;

If you want a solution for SQL 2000 or ANSI, try this:

declare @numRecs int, @numBuckets int, @recsPerBucket int;
select @numRecs = count(*) from @NAMES;
select @numBuckets = 3;
select @recsPerBucket = @numRecs / @numBuckets;

select n.ID, case when d1.minIdInBucket is null then null else n.NAME end as NAME
from @NAMES n
left join (
   select min(n2.ID) as minIdInBucket
   from (
      select n1.ID, n1.NAME,
        (
         select count(*) / @recsPerBucket
         from @NAMES n2
         where n2.ID < n1.ID
        ) as bucket
      from @NAMES n1
     ) n2
   group by n2.bucket
  ) d1
on  n.ID = d1.minIdInBucket
order by n.ID;
chadhoc
@Chadhoc: You have written 3 harcoded,I want the result to be divideed equally based on the total rows.
Vijjendra
The 3 is the number of buckets you want, will evenly partition the data in that number of buckets over the resultset. Not sure what you're getting at if you don't want this - maybe you mean you want to dynamically determine the number of buckets that should be used based on a certain number of rows per bucket? Either way, you have to provide some value to use. I'll update the code using a variable vs. the 3
chadhoc
+1 This splits the list in three dynamically. It's hard to see how you can do that without hardcoding that you want three parts.
Andomar
Maybe the OP wanted (but didn't say they wanted) the name to appear every 5 rows - which means, in this example, that there are 3 groups. However, that requires a better problem specification and perhaps a second example - where the total number of rows is, say, 23, and the ID numbers are not contiguous (there are gaps left by deleted rows). I suspect these are issues not considered by the OP.
Jonathan Leffler
@Jonathan - both solutions could easily be adapted to bucket into 5 rows/bucket (i.e. a simple calculation prior to the queries to calc the @numBuckets). As for ID numbers not being contiguous, either solution also accounts for and handles that scenario as well (i.e. there is no dependency on the ID numbers in any way to determine where/when to start a bucket and/or display a name)...only thing the ID numbers are used for is sorting the output, not calculating buckets/name display/etc.
chadhoc
A: 

You could calculate the row number and the total number of rows in a subquery. The outer select can then choose based on those columns:

select 
    id,
    case 
        when id = 1 then name 
        when id = total/3+1 then name 
        when id = total*2/3+1 then name 
        else '' 
    end
from (
    select row_number() over (order by id) as nr,
        (select count(*) from @names) as total,
        *
    from @names
) sub
Andomar