SQLServer Pivot query - sql-server-2008

I am working on a appliaction in which I have a following schema
Tasks Master
Task_ID
Task_Name
Task_Details
Task_ID
Task_Date
Task_Count (can be any number like 2 or 3 or 4 or 40)
the Input Form is like that which the staff will fill at the end of the day.
Date | Task Name | Task_Count
24/01/2010 | How many cheque books issued today | 12
24/01/2010 | How many ATM Issued today | 7
Now I want a matrix report showing all tasks suppose 28 tasks in vertical row and on given month it should show all the dates of the particular month horizontal direction like from 1 to 31 days or 30 or 28 as per month days with the task_count using PIVOT in query. i am failed to produce the result as i dont know to make it work. please help.
thanks

You'll need to use a dynamic query since the columns returned by the pivot changes each month.
With some time this could probably be made more elegant, but here's the basic idea:
declare #StartOfMonth datetime = '11/1/2010';
declare #counter datetime = #StartOfMonth;
declare #sql varchar(MAX) = '';
declare #columnnames varchar(MAX);
declare #columnfilter varchar(MAX);
declare #fieldname varchar(12);
--First, create a string of dynamic columns, one for each day of the month.
WHILE (MONTH(#counter) = MONTH(#StartOfMonth))
BEGIN
SET #fieldname = '[' + CONVERT(varchar(10), #counter, 101) + ']';
--Wrap the columns in ISNULL(#,0) to avoid having null values for days without tasks.
SET #columnnames = ISNULL(#columnnames + ',', '') + 'ISNULL(' + #fieldname + ',0) AS ' + #fieldname;
--Also create a dynamic list of the Task_Date values to include in the pivot.
SET #columnfilter = ISNULL(#columnfilter + ',', '') + #fieldname;
SET #counter = DATEADD(DAY,1,#counter);
END
--Put it all together into a pivot query.
set #sql = 'SELECT Task_Name, ' + #columnnames + ' FROM (';
set #sql = #sql + 'SELECT M.Task_Name, D.Task_Date, D.Task_Count '
set #sql = #sql + 'FROM Task_Detail D JOIN Task_Master M ON D.Task_ID = M.Task_ID) as SourceTable ';
set #sql = #sql + 'PIVOT (SUM(Task_Count) FOR Task_Date IN (' + #columnfilter + ')) AS PivotTable';
exec (#sql)

Related

Incorrect syntax near '2' at line 11. line 11 is "#processdate" variable [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I'm getting syntax error in #processdate variable, AS per my understanding it is because of date format (sub query that is stored in #processdate variable). I need help AS I am new to sql.
I'm trying to SET all of the query in stored procedure. What I have tried - dIFferent date formats, converting #processdate variable.
ALTER PROCEDURE [dbo].[rec_search_pay]
#processdatefrom DATETIME,
#processdateto DATETIME,
#collectiondatefrom DATETIME,
#collectiondateto DATETIME,
#amount MONEY,
#stationid VARCHAR(10),
#bankcode VARCHAR(2),
#branchcode VARCHAR(6),
#ponumber VARCHAR(10)
AS
BEGIN
DECLARE #bank AS VARCHAR(MAX);
DECLARE #branch AS VARCHAR(MAX);
DECLARE #station AS VARCHAR(MAX);
DECLARE #amounts AS VARCHAR(MAX);
DECLARE #pono AS VARCHAR(MAX);
DECLARE #processdate AS NVARCHAR(MAX);
DECLARE #collectiondate AS NVARCHAR(MAX);
DECLARE #stmt AS VARCHAR(MAX);
DECLARE #linebreak AS VARCHAR(2);
SET #linebreak = CHAR(13) + CHAR(10);
SET #processdate = ' AND h.processdate >= ' + CAST(#processdatefrom AS NVARCHAR) +
' AND h.processdate <= ' + CAST(#processdateto AS NVARCHAR)
SET #collectiondate = ' AND h.collectiondate >= ' + CAST(#collectiondatefrom AS NVARCHAR) +
' AND h.collectiondate <= ' + CAST(#collectiondateto AS NVARCHAR)
IF (#amount LIKE '%[^0-9]')
SET #amounts = ' AND amount = ' + #amount
ELSE
SET #amounts = ' '
IF (#bankcode <> '00')
SET #bank = ' and h.bankcode = ' + #bankcode
ELSE
SET #bank = ' '
IF (#branchcode <> '0')
SET #branch = ' and h.branchcode = ' + #branchcode
ELSE
SET #branch = ' '
IF (#stationid <> 7)
SET #station = ' and h.stationid = ' + #stationid
ELSE
SET #station = ' '
IF (#ponumber LIKE '%[^0-9]')
SET #pono = ' and h.advicenumber = ' + #ponumber
ELSE
SET #pono = ' '
SET NOCOUNT ON;
SET #stmt = 'SELECT h.bankcode, RTRIM(bankname) AS bankname, h.branchcode,
RTRIM(branchname) AS branchname, h.processdate,
h.collectiondate, h.advicenumber, h.amount, h.commission,
h.servicecharges, h.others, h.adjustment, s.stationname, h.userid
FROM banks b, branches br, reconcile h, stations s
WHERE CAST(h.bankcode AS VARCHAR) = b.bankcode
AND CAST(h.bankcode AS VARCHAR) = br.bankcode
AND CAST(h.branchcode AS VARCHAR) = br.branchcode
AND h.stationid = s.stationid
AND returned <> ''Y''
'+ #processdate + #linebreak + --problems seems to be here
+ #collectiondate + #linebreak +
+ #bank + #linebreak +
+ #branch + #linebreak +
+ #station + #linebreak +
+ #amounts + #linebreak +
+ #pono + #linebreak +
'ORDER BY 1, 3, collectiondate'
EXEC (#stmt)
Executing the stored procedure:
exec rec_search_pay '2018-mar-02', '2018-may-01', '2018-mar-01',
'2018-apr-30', 27698, 1, 3, 2003, 4721621
Expected result:
3 /NATIONAL BANK OF PAKISTAN /2003 /C.O.D. DRIGH ROAD [2003]
/2018-04-05 00:00:00.000 /2018-04-02 00:00:00.000 /4721621 /27698.00
/272.00 /0.00 /0.00 /0.00 /Karachi /john
Actual result:
Incorrect syntax near '2'.
Stored procedure(query) using print:
select h.bankcode, rtrim(bankname) AS bankname, h.branchcode,
rtrim(branchname) AS branchname, h.processdate, h.collectiondate,
h.advicenumber, h.amount, h.commission, h.servicecharges, h.others,
h.adjustment, s.stationname, h.userid
from banks b, branches br, reconcile h, stations s
Where cast (h.bankcode AS VARCHAR) = b.bankcode
and cast (h.bankcode AS VARCHAR) = br.bankcode
and cast (h.branchcode AS VARCHAR) = br.branchcode
and h.stationid = s.stationid
and returned <> 'Y'
and h.processdate >= Mar 2 2018 12:00AM and h.processdate <= --prob
May 1 2018 12:00AM
and h.collectiondate >= Mar 1 2018 12:00AM and h.collectiondate <=
Apr 30 2018 12:00AM
and h.bankcode = 3
and h.branchcode = 2003
and h.stationid = 1
order by 1, 3, collectiondate
When you specify date literals in your query, the date value must be surrounded with single quotes. For example this part of your code:
and h.processdate >= Mar 2 2018 12:00AM
If you add quotes, it should become:
and h.processdate >= 'Mar 2 2018 12:00AM'
But it isn't a good idea to use date formats like this. You better use '20180302' for March 2nd, 2018. This will save you a lots of trouble.

How to round numbers within dynamic pivot?

So i have this:
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #ColumnName= ISNULL(#ColumnName + ',','')
+ QUOTENAME(period)
FROM (SELECT DISTINCT period FROM atbv_Accounting_Transactions WHERE lAccountNO LIKE '6%' AND Period LIKE '2017%') AS Periods
SET #DynamicPivotQuery =
N'SELECT lAccountNo, ' + #ColumnName + '
FROM (SELECT
lAccountNo, period, SUM(Amount) As Total
FROM atbv_Accounting_Transactions
WHERE lAccountNO LIKE ''6%'' AND Period LIKE ''2017%''
GROUP BY lAccountNo, period
) AS T
PIVOT(SUM(TOTAL)
FOR period IN (' + #ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery
It returns me this:
How do i remove decimal places within select itself. I cannot edit column in the table and reduce the decimal places.So i need to edit this query to return values without decimals.
Thanks!
Should just change SUM(Amount) to cast(SUM(Amount) as int) or perhaps floor(SUM(Amount)) and it will do the trick.

Find minimum and maximum column value of a common column across all tables in a SQL Server 2008 database

I am examining a third party SQL Server 2008 database. In this database, there are 2 columns CREATED_DATETIME and UPDATED_DATETIME, which are present in majority of the tables, but probably not all.
I want to find the minimum and maximum value of these 2 columns across all tables in the database which have these 2 columns. That will give me a fair idea that the data in the database is from which period to which period.
How can I write such a query?
Something like the following should work
DECLARE #C1 AS CURSOR,
#TABLE_SCHEMA SYSNAME,
#TABLE_NAME SYSNAME,
#HasCreated BIT,
#HasUpdated BIT,
#MaxDate DATETIME,
#MinDate DATETIME,
#SQL NVARCHAR(MAX)
SET #C1 = CURSOR FAST_FORWARD FOR
SELECT TABLE_SCHEMA,
TABLE_NAME,
COUNT(CASE
WHEN COLUMN_NAME = 'CREATED_DATETIME' THEN 1
END) AS HasCreated,
COUNT(CASE
WHEN COLUMN_NAME = 'UPDATED_DATETIME' THEN 1
END) AS HasUpdated
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME IN ( 'CREATED_DATETIME', 'UPDATED_DATETIME' )
GROUP BY TABLE_SCHEMA,
TABLE_NAME
OPEN #C1;
FETCH NEXT FROM #C1 INTO #TABLE_SCHEMA , #TABLE_NAME , #HasCreated , #HasUpdated ;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = N'
SELECT #MaxDate = MAX(D),
#MinDate = MIN(D)
FROM ' + QUOTENAME(#TABLE_SCHEMA) + '.' + QUOTENAME(#TABLE_NAME) + N'
CROSS APPLY (VALUES ' +
CASE WHEN #HasCreated = 1 THEN N'(CREATED_DATETIME),' ELSE '' END +
CASE WHEN #HasUpdated = 1 THEN N'(UPDATED_DATETIME),' ELSE '' END + N'
(#MaxDate),
(#MinDate)) V(D)
'
EXEC sp_executesql
#SQL,
N'#MaxDate datetime OUTPUT, #MinDate datetime OUTPUT',
#MaxDate = #MaxDate OUTPUT,
#MinDate = #MinDate OUTPUT
FETCH NEXT FROM #C1 INTO #TABLE_SCHEMA , #TABLE_NAME , #HasCreated , #HasUpdated ;
END
SELECT #MaxDate AS [#MaxDate], #MinDate AS [#MinDate]
select MIN(CREATED_DATETIME) MinCREATED_DATETIME_Table1, MAX(CREATED_DATETIME) MaxCREATED_DATETIME_Table1, MIN(CREATED_DATETIME) MinCREATED_DATETIME_Table2, MAX(CREATED_DATETIME) MaxCREATED_DATETIME_Table2 from Table1, Table2
Run this script in SSMS (CTrl+T=text results, F5=execute query):
SET NOCOUNT ON;
SELECT 'SELECT MIN('
+ QUOTENAME(c.COLUMN_NAME)
+ ') AS '
+ QUOTENAME('Min '+c.TABLE_NAME+'.'+c.COLUMN_NAME)
+ ', MAX('
+ QUOTENAME(c.COLUMN_NAME)
+ ') AS '
+ QUOTENAME('Max_'+c.TABLE_NAME+'.'+c.COLUMN_NAME)
+ CHAR(13)
+ 'FROM ' + QUOTENAME(c.TABLE_SCHEMA)+'.'+QUOTENAME(c.TABLE_NAME)
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE c.COLUMN_NAME IN ('CREATED_DATETIME', 'UPDATED_DATETIME')
ORDER BY c.TABLE_SCHEMA, c.TABLE_NAME, c.COLUMN_NAME;
SET NOCOUNT OFF;
It will generate another script. Execute generated script.
Example (generated script for master database and all tables with low column name > WHERE c.COLUMN_NAME IN (N'low')):
SELECT MIN([low]) AS [Min spt_fallback_dev.low], MAX([low]) AS [Max_spt_fallback_dev.low]
FROM [dbo].[spt_fallback_dev]
SELECT MIN([low]) AS [Min spt_values.low], MAX([low]) AS [Max_spt_values.low]
FROM [dbo].[spt_values]
Run the script mentioned in the link below. You will have to slightly alter as per your requirements.
SCRIPT to Search every Table and Field

How to get rows that are less than or greater than in dynamic SQL?

I have a dynamic SQL where it has a information about the person, for e.g.
Table
ID Name Address Age(int)
1 a Main 30
2 b CT 35
Now how can I get all Person their Age < 40 or Age >= 30 or Age <= 50 etc. What I am trying to do is pass " <30 " or " >40 " etc as parameter and query accordingly.
Assuming you are using MSSQL, you can use EXEC to achieve this indirection. For example:
DECLARE #age_constraint AS VARCHAR(100) = '<30'
DECLARE #query AS VARCHAR(255)
SET #query = 'SELECT * FROM Table WHERE Age' + #age_constraint
EXEC(#query)
See http://msdn.microsoft.com/en-us/library/ms188332.aspx for more info.
One thing I'm curious about is whether you're scripting this out yourself or if you're having a UI component do it. The main issue with the latter is that you introduce SQL injection issues if you allow users to just type in "<40" for instance.
To help prevent that and make sure your datatypes make sense for the columns you're checking, you can use sp_executesql, but you'd have to separate out your operator from your numeric value. Something along these lines should work:
DECLARE #sql NVARCHAR(4000), #parameterlist NVARCHAR(500), #ageParameter INT, #equality NVARCHAR(2)
SET #ageParameter = 40
SET #equality = '='
SET #sql =
'SELECT * FROM SomeTable
WHERE #equality = ''='' AND Age = #ageParameter
UNION ALL
SELECT * FROM SomeTable
WHERE #equality = ''<'' AND Age < #ageParameter
UNION ALL
SELECT * FROM SomeTable
WHERE #equality = ''>'' AND Age > #ageParameter
'
SET #parameterlist = '#ageParameter INT, #equality NVARCHAR(2)'
EXEC sp_executesql #sql, #parameterlist, #ageParameter = #ageParameter, #equality = #equality
More information on how sp_executesql works: http://msdn.microsoft.com/en-us/library/ms188001(v=sql.100).aspx

INSERT INTO dynamically added columns

Running SQL Server 2005/2008, I am rewriting my query to be cleaner and more compliant to not include bad habits. I used to have lots of IF statements and PIVOT to do this, but found a better way to achieve it now and just need a last bit to make it almost perfect.
DECLARE #startdate DATETIME;
DECLARE #enddate DATETIME;
DECLARE #showstore INT;
DECLARE #showcashier INT;
DECLARE #showregister INT;
DECLARE #showdate INT;
DECLARE #sql NVARCHAR(MAX);
DECLARE #result0 NVARCHAR(MAX);
SET #startdate = '1/1/2012';
SET #enddate = '2/28/2013';
SET #showdate = 1;
SET #showstore = 0;
SET #showcashier = 1;
SET #showregister = 0;
SET #startdate = DATEADD(DAY, DATEDIFF(DAY, 0, #startdate), 0);
SET #enddate = DATEADD(DAY, DATEDIFF(DAY, 0, #enddate), 0);
SET #sql = N'CREATE TABLE ##a13 (' + SUBSTRING(
CASE WHEN #showdate = 1 THEN ',[Transaction Date] DATETIME' ELSE '' END +
CASE WHEN #showstore = 1 THEN ',[Store ID] VARCHAR(10)' ELSE '' END +
CASE WHEN #showcashier = 1 THEN ',[Cashier] VARCHAR(100)' ELSE '' END +
CASE WHEN #showregister = 1 THEN ',[Register] VARCHAR(20)' ELSE '' END, 2, 2000);
DECLARE myCursor CURSOR FOR
SELECT DISTINCT c.CurrencyDesc
FROM dbo.Currencies AS c INNER JOIN dbo.rpPay AS p ON c.POSCurrency = p.PayType
INNER JOIN dbo.RPTrs AS r ON r.ReceiptNO = p.ReceiptNo
WHERE
c.CurrencyDesc <> 'Testing' AND c.CurrencyDesc <> 'Cash Change' AND
r.TRSDate >= #startdate AND r.TRSDate <= #enddate
OPEN myCursor
FETCH NEXT FROM myCursor INTO #result0
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = #sql + ',[' + #result0 + '] INT'
FETCH NEXT FROM myCursor INTO #result0
END
CLOSE myCursor
DEALLOCATE myCursor
SET #sql = #sql + ')'
EXECUTE sp_executesql #sql, N'#startdate DATETIME,
#enddate DATETIME',#startdate, #enddate;
SET #sql = 'SELECT * FROM ##a13; DROP TABLE ##a13'
EXECUTE sp_executesql #sql
This Returns a table empty of rows. (know that the Currencies tables has more CurrencyDesc then shown here because these are just the ones used in the date range provided)
Which is exactly what I expect from it. Great so far so good. Now I need to add rows of data to it based on a Date Range (#startdate >= and <= #enddate) and depending on what they have checked off from the 4 possible options (#showstore, #showcashier, #showdate, #showregister)
Example : Date from 1/1/2013 till 2/28/2013 and show Register only (as seen in the picture) should have this DATA :
| Register | Cash | House Acct | MasterCard | Visa/MC
--------------------------------------------------------
1 | 01 | 20.00 | 235.25 | 1235.32 | 135.23
2 | 02 | 30.00 | 3542.42 | 323.52 | 523.64
3 | 03 | 23.35 | 100.32 | 3267.24 | 235.25
Reason for 2005/2008 is because some of the clients this is executed against, still use 2005 and in order to use PIVOT I would have to change the compatibility level on each database that is 2005.
PS. Before I get yelled at again, if I use #a13 instead of the global ##a13 it gives me
Msg 208, Level 16, State 0, Line 1
Invalid object name '#a13'.
What can I do about that so I don't use global temp tables?
If I am incorrect here, please clarify.
I BELIEVE you are asking how to populate a table with dynamic columns based on user input. The right answer here is, don't do that!
The best practice for this kind of thing is to have ALL the fields in your output table, then in your application/display layer you only show the fields that the user has requested.
Customizing a table layout within TSQL just to make a clean presentation introduces a lot of unnecessary complexity. This complexity comes with an increased performance cost as well.
If you have a static output table then it's trivial to return your data using the parameters given.