November 9, 2006 at 6:48 am
Hello friends!
i need your help...
i am creating report for my company..i am getting following output from my stored procedure. This is for weekly report
Request----------By User--------By Admin
10/15-11/21--------13-------------3
10/22-10/28--------6---------------8
10/29-11/04--------10-------------15
11/05-11/09---------8--------------6
but i want output by cross-tab query like
Request----10/15-11/21---10/22-10/28---10/29-11/04----11/05-11/09
By User---------13-------------6--------------10--------------8
By Admin---------3-------------8--------------15--------------6
please help me out
Regards,
Papillon
November 9, 2006 at 2:30 pm
Use the CHAR function. I think it is CHAR(9). Please check it.
November 9, 2006 at 3:43 pm
If you are using SQL2005, you could use the PIVOT() function. If you aren't you can get around this using dynamic SQL.
Give me a few minutes, and I'll code up an example
SQL guy and Houston Magician
November 9, 2006 at 4:32 pm
how about this:
Be careful of overflowing the 4000 byte variables and if there are more values for request you could expand this out to generate the insert statements dynamically too.
--Example data
CREATE TABLE #table1
(
Date SMALLDATETIME,
ByUser INT,
ByAdmin INT
)
INSERT INTO #table1(date, byuser,byadmin)
VALUES('2006-11-09', 2, 2)
INSERT INTO #table1(date, byuser,byadmin)
VALUES('2006-11-08', 4, 4)
INSERT INTO #table1(date, byuser,byadmin)
VALUES('2006-11-07', 8, 4)
INSERT INTO #table1(date, byuser,byadmin)
VALUES('2006-11-06', 1, 12)
--Report
DECLARE @ResTab NVARCHAR(1000) --Generate Results Table
DECLARE @Insert1 NVARCHAR(4000) --Insert Into Results Table
DECLARE @Insert2 NVARCHAR(4000) --Insert into Results table
DECLARE @Results NVARCHAR(4000) --Select Results
DECLARE @drop NVARCHAR(50) --Drop Table
SELECT @ResTab = COALESCE(
@ResTab+ N', ',
N'CREATE TABLE ##Results' + CAST(@@SPID AS NVARCHAR(3)) + ' (Source VARCHAR(10), '
) + '[' + CONVERT(CHAR(8), Date, 112) + N'] INT',
@Insert1 = COALESCE(
@Insert1 + N', ' + CHAR(13) + ' ',
N'INSERT INTO ##Results' + CAST(@@SPID AS NVARCHAR(3)) + CHAR(13) + 'SELECT ''ByUser'' AS Source, '
) + 'SUM(CASE WHEN DATE = ''' + CONVERT(NCHAR(8), Date, 112) +
''' THEN ByUser ELSE 0 END) AS [' + CONVERT(NCHAR(8), Date, 112) + ']',
@Insert2 = COALESCE(
@Insert2 + N', ' + CHAR(13) + ' ',
N'INSERT INTO ##Results' + CAST(@@SPID AS NVARCHAR(3)) + CHAR(13) + 'SELECT ''ByAdmin'' AS Source, '
) + 'SUM(CASE WHEN DATE = ''' + CONVERT(NCHAR(8), Date, 112) +
''' THEN ByAdmin ELSE 0 END) AS [' + CONVERT(NCHAR(8), Date, 112) + ']',
@Results = COALESCE(
@Results + N', ',
N'SELECT SOURCE AS Request, '
) + 'SUM([' + CONVERT(NCHAR(8), Date, 112) + ']) AS [' + CONVERT(NCHAR(12), Date, 110) + ']'
FROM #Table1
GROUP BY DATE
ORDER BY DATE
--WHERE Date between x AND y
SELECT @ResTab = @ResTab + ')',
@Insert1 = @Insert1 + N' FROM #table1 GROUP BY Date', -- + 'WHERE Date BETWEEN X and Y'
@Insert2 = @Insert2 + N' FROM #table1 GROUP BY Date', -- + 'WHERE Date BETWEEN X and Y'
@Results = @Results + N' FROM ##Results' + CAST(@@SPID AS NVARCHAR(3)) + CHAR(13) + 'GROUP BY Source',
@drop = 'DROP TABLE ##Results' + CAST(@@SPID AS NVARCHAR(3))
EXEC(@resTab)
EXEC(@Insert1)
EXEC(@Insert2)
EXEC(@Results)
EXEC(@Drop)
DROP TABLE #Table1
SQL guy and Houston Magician
November 9, 2006 at 6:10 pm
And, be aware that because the code uses a global temp table, you should do something to guarantee that only one instance of the code will ever be executed at the same time of you might come up with some surprising results...
--Jeff Moden
Change is inevitable... Change for the better is not.
November 9, 2006 at 6:11 pm
Do you have an example?
--Jeff Moden
Change is inevitable... Change for the better is not.
November 9, 2006 at 9:27 pm
Papillon,
Would you post the query that makes the output, please... there may be a trick or two we can pull off on this one...
--Jeff Moden
Change is inevitable... Change for the better is not.
November 9, 2006 at 9:41 pm
hi
pls find here
CREATE Proc Em_SubmitReport --'W','10/15/2006','11/09/2006'
@type varchar(10),
@fromdt varchar(12),
@todt varchar(12)
AS
DECLARE @QUERY VARCHAR(8000)
DECLARE @max-2 DATETIME
DECLARE @min-2 DATETIME
DECLARE @Wek INT
DECLARE @Yrs INT
DECLARE @DateFormat VARCHAR(50)
DECLARE @User INT
DECLARE @admin-2 INT
declare @StDate datetime
declare @EdDate datetime
create table #Temp(col1 varchar(50),col2 int,col3 int)
IF @type = 'W' --Weekly Report for selected Dates
BEGIN
SET @QUERY = 'DECLARE CUR_1 CURSOR FOR select CalWeek,CalYear, min(calDate)as ''min1'',max(calDate) as ''max1'' from Calendar where calDate between ''' + CONVERT(VARCHAR(12),@fromdt,101) + ''' and '''+ CONVERT(VARCHAR(12),@toDt,101) +''' and CalWeek IN (select CalWeek from Calendar where calDate between ''' + CONVERT(VARCHAR(12),@fromdt,101) + ''' and '''+ CONVERT(VARCHAR(12),@toDt,101) +''' group by CalWeek ) group by CalWeek,CalYear'
END
IF @type = 'M' -- Monthly report for selected dates
BEGIN
SET @QUERY = 'DECLARE CUR_1 CURSOR FOR select CalMonth,CalYear, min(calDate)as ''min1'',max(calDate) as ''max1'' from Calendar where calDate between ''' + CONVERT(VARCHAR(12),@fromdt,101) + ''' and '''+ CONVERT(VARCHAR(12),@toDt,101) +''' and CalMonth IN (select CalMonth from Calendar where calDate between ''' + CONVERT(VARCHAR(12),@fromdt,101) + ''' and '''+ CONVERT(VARCHAR(12),@toDt,101) +''' group by CalMonth ) group by CalMonth,CalYear'
END
IF @type = 'Y' -- Yearly report
BEGIN
SET @QUERY = 'DECLARE CUR_1 CURSOR FOR select 1,CalYear, min(calDate)as ''min1'',max(calDate) as ''max1'' from Calendar where calDate between ''' + CONVERT(VARCHAR(12),@fromdt,101) + ''' and '''+ CONVERT(VARCHAR(12),@toDt,101) +''' and CalYear IN (select CalYear from Calendar where calDate between ''' + CONVERT(VARCHAR(12),@fromdt,101) + ''' and '''+ CONVERT(VARCHAR(12),@toDt,101) +''' group by CalYear ) group by CalYear'
END
EXEC(@QUERY)
OPEN CUR_1
FETCH NEXT FROM CUR_1 INTO @Wek,@Yrs,@MIN,@MAX
WHILE @@FETCH_STATUS =0
BEGIN
set @DateFormat = NULL
set @User = NULL
set @admin-2 = NULL
select @DateFormat = CONVERT(VARCHAR(12), @min-2, 101) +'-'+ CONVERT(VARCHAR(12), @max-2, 101)
select @User = count(tktsubmittedby) from tickets where ( datecreated >= @min-2 and datecreated < @max-2 + 1) and SubmittedBy = 'U'
select @admin-2 = count(tktsubmittedby) from tickets where (datecreated >= @min-2 and datecreated < @max-2 + 1) and SubmittedBy = 'A'
INSERT INTO #Temp VALUES (@DateFormat,@User,@Admin)
FETCH NEXT FROM CUR_1 INTO @Wek,@Yrs,@MIN, @max-2
END
select Col1 [Total Ticket Submitted],col2 [By User],col3 [By Admin] from #Temp
CLOSE CUR_1
DEALLOCATE CUR_1
Regards,
Papillon
November 10, 2006 at 9:16 am
Hi Jeff, I agree with you on the global temp table. In the query I actually use the current SPID as part of the name. It's not perfect but I figure it's a weekly report so it's probably a pretty low risk anyway. It looks like this proc could sure use a once over too!
SQL guy and Houston Magician
November 10, 2006 at 7:01 pm
Robert,
In my haste, I missed the SPID thing. Clever to say the least. Thanks for the feedback.
--Jeff Moden
Change is inevitable... Change for the better is not.
November 10, 2006 at 9:17 pm
Looking at it now...
--Jeff Moden
Change is inevitable... Change for the better is not.
November 11, 2006 at 6:47 pm
Sorry folks... I had posted some code and there was an error in it because the date part for weeks is acting very odd... trying to see what's up with that... will repost when I fix it...
Apparently, I found a bug in MicroSoft's functions... this gives the wrong answer...
SELECT DATEADD(wk,DATEDIFF(wk,0,'01/30/2000'),0)
<insert sound of crickets chirping here>
<insert sound of rustling notes and frantic typing here>
<insert sounds of sailor swearing here>
Ok.. found the problem... I'd forgotten that the WK datepart goes through transition on the border between a Saturday and a Sunday (no matter what DATEFIRST is either!!). Since date 0 (01/01/1900) is a Monday, that causes an offset by one. You have to use -1 instead of 0 on the DATEADD/DATEDIFF calculation for weeks... Like this...
SELECT DATEADD(wk,DATEDIFF(wk,-1,'01/30/2000'),-1)
The corrected code is in the next message.
--Jeff Moden
Change is inevitable... Change for the better is not.
November 11, 2006 at 8:37 pm
Thought I'd throw a different slant at this one because it's an "open ended" report with no definite constraints on how many reporting periods may be requested within the start and end dates. I knew it would be a lot of fun especially trying to do it in a set-based fashion without the limits of dynamic SQL (that was the extra fun) because that would really limit how many "columns" could be output
This code replaces both the (ugh!) cursor bearing stored procedure that Papillon made and it creates a tab delimited output suitable for display in either a word processor or a spreadsheet. I didn't convert it to a stored proc but that is simple to do... Papillon, I did change the names of the variables used as "parameters"... you may have to change them back to keep from breaking code if you convert this to a proc.
I tested this code against a million row "ticket" table (probably real overkill considering it's for "tickets") and it returns most reports in just a second or two (composit index must be included on CreatedDate/SubmittedBy columns). I also added some extra "goodies" to the report... This one also reports on "Quarters" as well as including full subtotals and the grand total. It also does some checking/manipulation of the input dates if the enddate is missing or the dates have been sent out of order. AND, just for grins, I right aligned all the numbers.
There is a limit as to how many report "periods" will be produced before this runs into a stone wall, but that's more than 650 columns wide (more than 12 years of a by-the-week report).
This one was written for SQL Server 2000. Obviously, using the Pivot function of SQL Server 2005 would be a little easier (the report is NOT the hard part) and probably offer a wider report (storable in a table, as well?) than this one offers. It's also worth mentioning that importing the contents of the working table (a single temp table) this thing creates into a spreadsheet and pivoting things there might be a little better because of the formatting available in spreadsheets.
And, finally, there's lots of documentation in the code that's worth reading...
Use the text output when running these, folks...
First... here's the test data I used...
--===== Create and populate a million row test table. A "real life"
-- example would be much wider but this will suffice for test data
-- and is easy to write a comparison test for in an "application".
SELECT TOP 1000000
IDENTITY(INT,1,1) AS RowNum,
CAST(RAND(CAST(NEWID() AS VARBINARY))*50000+1 AS INT) AS UserID,
CHAR(STR(RAND(CAST(NEWID() AS VARBINARY))*25+65))
+ CHAR(STR(RAND(CAST(NEWID() AS VARBINARY))*25+65)) AS SubmittedBy,
'A column for kicks' AS Kicks,
'Still another column just for proofing' AS StillAnother,
CAST(RAND(CAST(NEWID() AS VARBINARY))*100 AS DECIMAL(18,9)) AS SomeNumber,
CAST(RAND(CAST(NEWID() AS VARBINARY))*3653.0+36524.0 AS DATETIME) AS DateCreated --(>=01/01/2000 <01/01/2010)
INTO dbo.Tickets
FROM Master.dbo.SysColumns sc1,
Master.dbo.SysColumns sc2
--===== A table is not properly formed unless a Primary Key has been assigned
ALTER TABLE dbo.Tickets
ADD PRIMARY KEY CLUSTERED (RowNum)
--===== "warp" the data in a column to simulate requirements
UPDATE Tickets
SET SubmittedBy = CASE WHEN SubmittedBy <= 'PP' THEN 'U' ELSE 'A' END
--===== ...and, we'll do a little code optimization by adding an index to
-- support the upcoming query.
CREATE NONCLUSTERED INDEX BigTest_UserID_SomeValue
ON dbo.Tickets (DateCreated,SubmittedBy)
GO
... and here's the code to replace both the original stored procedure and produce the report...
-------------------------------------------------------------------------------------------
--===== These variables simulate the parameters of a stored proc
DECLARE @pStartDate DATETIME
DECLARE @pEndDate DATETIME
DECLARE @pRptType CHAR(1) --Can be W, M, Q, or Y
SET @pStartDate = '06/01/2000'
SET @pEndDate = '01/31/2000' --NULL
SET @pRptType = 'w'
--=====================================================================================================================
-- Preset environment, parameters, and variables
--=====================================================================================================================
--===== Suppress the autodisplay of row counts for appearance/speed and keep from giving GUI's false returns
SET NOCOUNT ON
--===== Declare local working variables
DECLARE @Periods INT --Holds the number of periods for the give report type
DECLARE @SwapDate DATETIME --Holds one of the input dates when dates are out of order and need to be swapped
--===== Declare the report output and formating variables
DECLARE @rptPeriod VARCHAR(8000)
DECLARE @rptByUser VARCHAR(8000)
DECLARE @rptByAdmin VARCHAR(8000)
DECLARE @rptTotal VARCHAR(8000)
DECLARE @DataColumnSize INT --Holds the maximum column size required for formatted output of the data
SET @DataColumnSize = 11 --Big enough for all the period range dates to display fully
DECLARE @NameColumnSize INT --Holds the maximum column size required for formatted output of the Row Names
SET @NameColumnSize = 9 --Big enough for all the Row Names to display fully
--===== If there is no end date, make it the same as the start date
IF @pEndDate IS NULL
SET @pEndDate = @pStartDate
--===== If the dates are in the wrong order, swap them
IF @pStartDate > @pEndDate
SELECT @SwapDate = @pStartDate,
@pStartDate = @pEndDate,
@pEndDate = @SwapDate
--===== Populate/change variables based on the report type
-- Will always have at least 1 period
SET @Periods = CASE @pRptType
WHEN 'W' THEN DATEDIFF(wk,@pStartDate,@pEndDate)+1
WHEN 'M' THEN DATEDIFF(mm,@pStartDate,@pEndDate)+1
WHEN 'Q' THEN DATEDIFF(qq,@pStartDate,@pEndDate)+1
WHEN 'Y' THEN DATEDIFF(yy,@pStartDate,@pEndDate)+1
END
-- Change the start date to the first day of the closest period
SET @pStartDate = CASE @pRptType
WHEN 'W' THEN DATEADD(wk,DATEDIFF(wk,-1,@pStartDate),0) --Always returns a Monday
WHEN 'M' THEN DATEADD(mm,DATEDIFF(mm,0,@pStartDate),0)
WHEN 'Q' THEN DATEADD(qq,DATEDIFF(qq,0,@pStartDate),0)
WHEN 'Y' THEN DATEADD(yy,DATEDIFF(yy,0,@pStartDate),0)
END
-- Change the end date to the last day of the closest period
SET @pEndDate = CASE @pRptType
WHEN 'W' THEN DATEADD(wk,DATEDIFF(wk,-1,@pEndDate)+1,0)-1 --Always returns a Sunday
WHEN 'M' THEN DATEADD(mm,DATEDIFF(mm,0,@pEndDate)+1,0)-1
WHEN 'Q' THEN DATEADD(qq,DATEDIFF(qq,0,@pEndDate)+1,0)-1
WHEN 'Y' THEN DATEADD(yy,DATEDIFF(yy,0,@pEndDate)+1,0)-1
END
--=====================================================================================================================
-- Create and prepopulate an "empty" work table for the correct number of periods.
-- This replaces the calendar table you were using and makes life pretty simple
--=====================================================================================================================
--===== If the working table exists, drop it
IF OBJECT_ID('TempDB..#Work') IS NOT NULL
DROP Table #Work
--===== Create the working table according to scale
-- Restrict the number of rows to process to the number of periods (like a programmable TOP)
SET ROWCOUNT @Periods
-- Create an "empty" work table with the correct number of periods
SELECT Period = IDENTITY(INT,0,1),
StartDate = CAST(NULL AS DATETIME),
EndDate = CAST(NULL AS DATETIME),
PeriodDisp = CAST(NULL AS CHAR(11)),
ByUser = CAST(0 AS INT),
ByAdmin = CAST(0 AS INT),
Total = CAST(0 AS INT)
INTO #Work
FROM Master.dbo.SysColumns WITH (NOLOCK)
-- Allow unrestricted processing again
SET ROWCOUNT 0
--===== Add a Primary Key because every table deserved one
ALTER TABLE #Work
ADD PRIMARY KEY (Period)
--=====================================================================================================================
-- Populate the work table with everything we need
--=====================================================================================================================
--===== Create the Start and End Dates for each period in the table so we can simplify the next bit of code
UPDATE #Work
SET StartDate = CASE @pRptType
WHEN 'W' THEN DATEADD(wk,DATEDIFF(wk,-1,@pStartDate)+Period,0) --Always returns a Monday
WHEN 'M' THEN DATEADD(mm,DATEDIFF(mm,0,@pStartDate)+Period,0)
WHEN 'Q' THEN DATEADD(qq,DATEDIFF(qq,0,@pStartDate)+Period,0)
WHEN 'Y' THEN DATEADD(yy,DATEDIFF(yy,0,@pStartDate)+Period,0)
END,
EndDate = CASE @pRptType
WHEN 'W' THEN DATEADD(wk,DATEDIFF(wk,-1,@pStartDate)+Period+1,0)-1 --Always returns a Sunday
WHEN 'M' THEN DATEADD(mm,DATEDIFF(mm,0,@pStartDate)+Period+1,0)-1
WHEN 'Q' THEN DATEADD(qq,DATEDIFF(qq,0,@pStartDate)+Period+1,0)-1
WHEN 'Y' THEN DATEADD(yy,DATEDIFF(yy,0,@pStartDate)+Period+1,0)-1
END
--===== Update the table with totals for each period and the period range to be used for display of each period
UPDATE #Work
SET ByUser = ISNULL(d.ByUser,0),
ByAdmin = ISNULL(d.ByAdmin,0),
Total = ISNULL(d.Total,0),
PeriodDisp = CONVERT(CHAR(5),w.StartDate,101)+'-'+CONVERT(CHAR(5),w.EndDate,101)
FROM #Work w
INNER JOIN
(--Derived table "d" accumulates counts according to the start date of each period
SELECT wi.StartDate,
ByUser = SUM(CASE WHEN t.SubmittedBy = 'U' THEN 1 ELSE 0 END),
ByAdmin = SUM(CASE WHEN t.SubmittedBy = 'A' THEN 1 ELSE 0 END),
Total = SUM(CASE WHEN t.SubmittedBy IN ('U','A') THEN 1 ELSE 0 END)
FROM dbo.Tickets t WITH (NOLOCK),
#Work wi
WHERE t.DateCreated >= wi.StartDate
AND t.DateCreated < wi.EndDate + 1
GROUP BY wi.StartDate
) d
ON w.StartDate = d.StartDate
--===== Create the total row
INSERT INTO #Work
(PeriodDisp, ByUser, ByAdmin, Total)
SELECT 'Total', SUM(ByUser), SUM(ByAdMin),SUM(Total)
FROM #Work
--=====================================================================================================================
-- Create the report lines
--=====================================================================================================================
--===== Create the period range line
SET @rptPeriod = LEFT('Period('+UPPER(@pRptType)+')'+SPACE(@NameColumnSize),@NameColumnSize)
SELECT @rptPeriod = @rptPeriod+CHAR(9)+PeriodDisp
FROM #Work
ORDER BY Period
--===== Create the ByUser line
SET @rptByUser = LEFT('By User'+SPACE(@NameColumnSize),@NameColumnSize)
SELECT @rptByUser = @rptByUser+CHAR(9)+STR(ByUser,@DataColumnSize)
FROM #Work
ORDER BY Period
--===== Create the ByAdmin line
SET @rptByAdmin = LEFT('By Admin'+SPACE(@NameColumnSize),@NameColumnSize)
SELECT @rptByAdmin = @rptByAdmin+CHAR(9)+STR(ByAdmin,@DataColumnSize)
FROM #Work
ORDER BY Period
--===== Create the Total line
SET @rptTotal = LEFT('Total:'+SPACE(@NameColumnSize),@NameColumnSize)
SELECT @rptTotal = @rptTotal+CHAR(9)+STR(Total,@DataColumnSize)
FROM #Work
ORDER BY Period
--=====================================================================================================================
-- Output the report
--=====================================================================================================================
PRINT 'Start Date:'+CHAR(9)+CONVERT(CHAR(11),@pStartDate,100)
PRINT 'End Date:' +CHAR(9)+CONVERT(CHAR(11),@pEndDate ,100)
PRINT 'Periods:' +CHAR(9)+STR(@Periods,@DataColumnSize)
PRINT ' '
PRINT @rptPeriod
PRINT SPACE(9)+CHAR(9)+REPLICATE(REPLICATE('=',@DataColumnSize)+CHAR(9),@Periods+1)
PRINT @rptByUser
PRINT @rptByAdmin
PRINT SPACE(9)+CHAR(9)+REPLICATE(REPLICATE('=',@DataColumnSize)+CHAR(9),@Periods+1)
PRINT @rptTotal
--=====================================================================================================================
-- Housekeeping
--=====================================================================================================================
--===== Drop the working table to conserve resources just in case the session doesn't end here
DROP TABLE #Work
--===================================================================================================================== p.s. CHAR(9) is the tab character
--Jeff Moden
Change is inevitable... Change for the better is not.
Viewing 13 posts - 1 through 12 (of 12 total)
You must be logged in to reply to this topic. Login to reply