0

I'm building an application in access for tracking projects. Like status, costs and how many hours are spent for each project. I've come a long way but I've stumbled upon a problem.

The data that I store in SQL is stored as follows:

 ID | EmployeeID |    Date    | Hours | Projectnumber | Remarks
 ---| ---------- | ---------- | ----- | ------------- | ----------
  1 |     1      | 2016-07-18 |   6   |    123456     | Many calls
  2 |     2      | 2016-07-17 |   2   |    654321     | 

Now I'd like my co-workers to see this data on a week to week basis, the most popular view is to have columns from Monday to Sunday. This means if somebody works the whole week on a specific project, you only get to see one line for this project.

In Access I managed to create this via a Crosstab query, but it is rather slow. I know that things speed up when using a stored procedure in SQL and so I tried to accomplish the same thing but with an SQL stored procedure + a pass-through query.

This is my code in MS ACCESS VBA:

"TRANSFORM Sum(U.hours) AS Hours " & _
"SELECT U.Projectnumber AS Project, W.Description as Description, F.Client As Client" & _
",(W.Adress & "" te "" & W.Town) as Adress, U.Remarks AS Remarks" & _
"FROM ((tblhours As U LEFT JOIN tblprojects As W ON U.Projectnumber = W.Projectnumber) " & _
"LEFT JOIN tblClients As F ON W.ClientID = F.ClientID) " & _
"WHERE (U.EmployeeID Like " & Me.cboEmployee & " " & _
"AND DatePart('ww',[U.Date],2,1) Like " & Me.cboWeek & " " & _
"AND Datepart('yyyy',[U.Date],2,1) like " & Me.cboYear & ") " & _
"GROUP BY U.Projectnumber, U.Remarks, W.Description, F.Client, W.Adress,W.Town " & _
"ORDER BY U.Date ASC PIVOT U.Date"

I have searched the internet for quite some time and ultimately put together this stored procedure in SQL Server 2016:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER Procedure [dbo].[usp_myprocedurename]
@EmployeeID int,
@week int,
@year int
AS
BEGIN

SET NOCOUNT ON

DECLARE @cols AS NVARCHAR(MAX);
DECLARE @query AS NVARCHAR(MAX);
DECLARE @prevquery as NVARCHAR(MAX)

SELECT @prevquery = '(SELECT 
        Sum(U.Hours) as Hours, U.Projectnumber AS Project, 
        W.Description as Description, F.Client As Client, 
        (W.Adress + '' te '' + W.Town) as Adress, U.Remarks AS Remarks,
        U.Date as Date
      FROM (tblHours As U LEFT JOIN tblProjects As W 
      ON U.Projectnumber = W.Projectnumber) 
      LEFT JOIN tblClients As F ON W.ClientID = F.ClientID
        WHERE U.EmployeeID Like ' + CONVERT(varchar(10), @EmployeeID) + ' AND DatePart(ww,U.Date) Like ' 
        + CONVERT(varchar(10), @week) + ' AND Datepart(yyyy,U.Date) like ' + CONVERT(varchar(10), @Year) +
      'GROUP BY U.Projectnumber, U.Remarks, W.Description, F.Client, W.Adress,W.Town, u.Date)'

SELECT @cols = STUFF((SELECT distinct 
                        ',' +
                        QUOTENAME(left(convert(nvarchar(30),Date),10))
                FROM tblHours

                WHERE EmployeeID Like CONVERT(varchar(10), @EmployeeID)
                AND DatePart(ww,Date) Like CONVERT(varchar(10), @week) AND Datepart(yyyy,Date) 
                like CONVERT(varchar(10), @Year)

            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)')  
        ,1,1,'');

SET @query =  ' SELECT Project, Description, Client, Adress, Remarks, ' + @cols + '
    FROM 
    (
    ' + @prevquery + '
    ) t
    PIVOT
    (
      SUM(Hours)
      FOR Date IN( ' + @cols + ') 
    ) p ';

    Execute(@query)
End

I hope it isn't all too messy, haha. Anyways, I have a picture of how this looks like in management studio with some random variables:

enter image description here

I thought, well this is great now I can continue building other things, but Access wasn't very happy with the change.

When I replace the current query in Access by a pass-through query which executes with the right variables it first gives me an ODBC error:

[Microsoft][SQL Server Native Client 11.0][SQL Server]SELECT failed because the following SET options have incorrect settings: 'CONCAT_NULL_YIELDS_NULL, ANSI_WARNINGS, ANSI_PADDING'. Verify that SET options are correct for use with indexed views and/or indexed on computed columns and/or filtered indexes and/or query notifications and/or XML data type methods and/or spatial indes operations. (#1934)

I click modify on my stored procedure and added the following:

SET CONCAT_NULL_YIELDS_NULL OFF
GO 
SET ANSI_WARNINGS OFF 
GO

When I run the query to alter the stored procedure and I restart Access the warning is gone. What is weird is that I have done this three times already. After the modification I click modify again to see nothing has actually changed?? Any ideas on that?

Anyways the error message is gone, hooray. But now for the real problem:

Pivot in access via stored procedure

As you can probably see in the picture, my decimal separator is a dot and the sum calculation below doesn't add up. I live in Holland where we use the comma as decimal separator, that's why the addition fails.

My other stored procedures work just fine, but with those I declare a table variable (no ODBC error). VBA also gives an error saying data type mismatch when summing those values.

How can I get access to see that the decimals in hours should be separated by a comma? Is it fixed by telling SQL which datatype the output data should be? If yes, please help me on how to achieve this. The hours are decimal(4,2) datatype in SQL by the way.

If there is something you can recommend me or if there's something you think me and others can profit from, please feel free to share this piece of information. I just recently started using SQL server, so any help is welcome.

==========

UPDATE

I think I've got it now;

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER Procedure [dbo].[usp_myprocedurename] 
@EmployeeID int, @week int, @year int AS BEGIN 

SET NOCOUNT ON 

DECLARE @cols nvarchar(max), @cols2 nvarchar(max), @Create nvarchar(max), @prevquery nvarchar(max), @query nvarchar(max) 

SELECT @cols = (SELECT distinct ',' + QUOTENAME(left(convert(nvarchar(30),Date),10)) + ' numeric(4,2)'  as [text()]
FROM tblHours WHERE employeeID Like CONVERT(varchar(10), @EmployeeID) AND DatePart(ww,date) Like CONVERT(varchar(10), @week) AND Datepart(yyyy,date) like CONVERT(varchar(10), @Year) 
FOR XML PATH('')); 

SELECT @cols2 = (SELECT distinct ',' + QUOTENAME(left(convert(nvarchar(30),Date),10)) as [text()]
FROM tblHours WHERE EmployeeID Like CONVERT(varchar(10), @EmployeeID) AND DatePart(ww,date) Like CONVERT(varchar(10), @week) AND Datepart(yyyy,Date) like CONVERT(varchar(10), @Year) 
FOR XML PATH('')) 

SELECT @prevquery = '(SELECT Sum(U.Hours) as Hours, U.Projectnumber AS Project, 
W.fldOmschrijving as Omschrijving, F.fldNaam As Opdrachtgever, 
(W.fldAdres + '''' te '''' + W.fldPlaats) as Adres, U.Remarks AS Remarks, U.Date as Date FROM (tblHours As U 
LEFT JOIN tblProjects As W ON U.Projectnumber = W.Projectnumber) 
LEFT JOIN tblClients As F ON W.ClientID = F.ClientID
WHERE U.Employeeid Like ' + CONVERT(varchar(10), @EmployeeID) + ' AND DatePart(ww,U.Date) Like ' + CONVERT(varchar(10), @week) + ' AND Datepart(yyyy,U.Date) like ' + CONVERT(varchar(10), @Year) + ' 
GROUP BY U.Projectnumber, U.Remarks, W.Description, F.Client, W.Adress,W.Town, u.Date)' 

SET @query =  ' SELECT Project, Description, Client, Adress, 
Remarks' + @cols2 + ' FROM (' + @prevquery + ') t 
PIVOT(SUM(Hours) FOR Date IN( ' + STUFF(@cols2,1,1,'') + ')) p '; 

EXEC('DECLARE @mytable table(Project int, Description nvarchar(255), 
Client nvarchar(255), Adress nvarchar(255), Remarks nvarchar(max)' + @cols + ') 
Insert Into @mytable(project, Description, Client, 
    adress, remark' + @cols2 + ')   
    Execute(''' + @query + ''') SELECT * FROM @mytable') 

End

It may not be the most efficient piece of SQL you've seen, but it seems to work now. Any comments on how to improve it are welcome ofcourse :)

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
  • What language region is your machine? Is it set to Netherlands to acknowledge the commas for decimals? – Parfait Aug 06 '16 at 01:05
  • Yes it is set to Netherlands. The decimal separator is perfectly translated to commas in my linked tables, so that shouldn't be the issue. I personally think that my stored procedure is outputting the hours as nvarchar. I wanted to try to insert it into a table variable so I could control the datatypes, but I couldnt get that to work with the columns as a variable (for example "set cols2 = day1WorkedInSpecificWeek as nvarchar(6), day2WorkedInSpecificWeek as nvarchar(6)" and then declare the table variable "DECLARE mytable table(myprojectnumber int, ..., cols2)" – Jordy Bekker Aug 06 '16 at 07:27
  • You are using `FOR XML PATH`. XML querying requires [certain connection variables to be ON](http://stackoverflow.com/q/3304018/11683). You keep setting them to OFF? – GSerg Aug 06 '16 at 12:34
  • Hmm, in my stored procedure i have quoted_identifier ON. I find it really wierd what happens. In acces I want to open my form that has a listbox containing the stored procedure as the source. When I open that form it asks me to login to my SQL server and when I do that I get the ODBC error. Here comes the wierd part; when I close and reopen my access application and open my form again I don't have to login and it works just fine. It has nothing to do with me turning the concat_null_yields_null and ansi_warnings off. Is it an idea to replace the "for xml path" or convert from XML? Also, how? – Jordy Bekker Aug 06 '16 at 13:43

1 Answers1

0

With the posted edit and with the help of comments it looks like I have what I want now! Thanks for the help :). The only thing I added to the code from my edit is a CAST function outside of the STUFF function where "FOR XML PATH" is in. I converted it to nvarchar(max). Thanks for steering me in the right direction!

  • ugh, I spoke too soon. It works for awhile and then it gives the same ODBC errormessage. Anybody has an idea? – Jordy Bekker Aug 06 '16 at 15:01
  • Make sure the mentioned connection variables are set to ON when you connect by setting the required ticks in your DSN that points to the SQL Server? – GSerg Aug 06 '16 at 17:23
  • I have no idea how I could achieve this, but I'm connecting to sql that is on my local machine. So no internet is needed :0 – Jordy Bekker Aug 06 '16 at 18:15
  • DSN is different from DNS. You said you have a pass-through connection from Access to SQL Server. Go to the properties of that connection. – GSerg Aug 06 '16 at 18:37
  • Oh sorry, at the DSN Configuration of SQL Server is both the "use ANSI quoted identifiers" and "Use ANSI nulls, paddings and warnings" checkbox ticked. I appreciate your help by the way :) – Jordy Bekker Aug 06 '16 at 18:47