on 2011 Jul 11 6:48 PM
I have run into an issue with SQL Expression Fields in CR 2008 when connecting to MS-SQL Serve 2008 R2 using either "OLE DB (ADO) Microsoft OLE DB Provider for SQL Server" or "SQL Server Native Client 10. 0".
If you use an instance of a table in the main report and then perform a summation on the same table name in an SQL expression using a field from the main report instance to limit the data set, the SQL expression does not seem to be able to tell that the limitation field came from the main report's data set. For instance:
( SELECT COUNT(*)
FROM dbo.tblGLAccounts
WHERE dbo.tblGLAccounts.GLAccountTypeID = tblGLAccounts.GLAccountTypeID
AND dbo.tblGLAccounts.GLAccountNumber < tblGLAccounts.GLAccountNumber
AND dbo.tblGLAccounts.BreakPoint = 1)
Normally this SQL expression would pull "tblGLAccounts.GLAccountTypeID" and 'tblGLAccounts.GLAccountNumber" from the main report's dataset instance of tblGLAccounts. Everything beginning with "dbo." would be data external to the report.
This works fine for any combination of CR 10, XI, XI R2, and MS-SQL Server versions 2000, 2005, 2008, and 2008 R2 . It also works for all versions and fix packs of CR 2008 with MS-SQL Server 2000, 2005, and 2008. The combination of CR 2008 (at least SP2 through SP4) and MS-SQL 2008 R2 does not work. The "tblGLAcounts" references get treated as if they were from the external data. This occurs whether the report is run from within the Crystal Reports Designer tool OR from a web application using the .Net runtimes.
The only way I have been able to fix this is to change the alias for the table in the man report.
( SELECT COUNT(*)
FROM dbo.tblGLAccounts
WHERE dbo.tblGLAccounts.GLAccountTypeID = tblGLAccounts_1.GLAccountTypeID
AND dbo.tblGLAccounts.GLAccountNumber < tblGLAccounts_1.GLAccountNumber
AND dbo.tblGLAccounts.BreakPoint = 1)
However, this is not an acceptable workaround as it will require manually adjusting thousands of customized customer reports.
I have tried replacing the CR 2008 .Net runtimes with "Crystal Reports for Visual Studio 2010". "CR For VS 2010" has the same issue with MS-SQL Server 2008 R2.
Any suggestions?
Request clarification before answering.
SELECT Statements should never be used in SQL Expressions. That's what Commands are for, SQL Expressions are for doing functions, not queries.
Thank you
Don
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Yes I know, Jason Long and I have had many discussions about this, Jason is a SQL Guru and used it also but he also prefers to use Commands to do the majority of the querying.... And there is no reason why you can't do the same. For a Developer the Command is available in code to edit where as SQL Expressions are not and never likely will be.
When you create the SQL Expression and use the formula editor and expand the functions you will not see SELECT any where.
So yes it did work but I tried to dissuade anyone from using SELECT because I knew one day it would catch up to those that did and not work... I can ping the developers to see if they are aware and what the issue may be but I need more details on the query you are using.
Also, SQL 2008 renamed their client dll to SQLNCLI10.dll ( SQL Server Native 10 ) in the driver list, are you using it? If not then please do so, it may "fix" the issue. Also requires CR 2008 Service 3 and Fix Pack 3.4 or Service Pack 4 to be installed, it has the update so it recognizes the new client engine.
Thanks
Don
I understand, but most of the time when I need to do something like this, recreating the entire report to pull entirely from a command just to accommodate a single subquery value is not feasible. Furthermore, 98% of our customers are not savvy or sophisticated enough in both SQL and CR to create their own commands. Our biggest selling point for CR is how easy it is for "non-IT" people to create there own custom, moderately sophisticated reports. Telling users that they will have to use "Commands" to perform some basic functionality that they will need sort of defeats the argument.
In any case, as I stated in my initial question, I have tested this with both the new and older SQL client DLLs. You and I have previously discussed the need for NC10 and Fixpack 3.4 with SQL 2008. I was being thorough in testing both clients in order to see if there was a difference in behavior. There isn't.
The query I used as an example is used to generate a positional count for a sub-set of account numbers based on a Boolean field named BreakPoint. Within the main report, the data is grouped by tblGLAccount.TypeID (Asset, expense, etc.) and then by AccountNumber. However, within a "type", the BreakPoint field indicates where subtotals should be calculated for all account numbers that occur before it. By counting how many breakpoints occur before the account number, within the type, that I am currently looking at and then grouping on that count within the TypeID group, I can generate the necessary subtotals within each account type in the report.
The full SQL query generated, with user inputs entered, looks like:
SELECT "tblGLAccounts"."GLAccountNumber", "tblGLAccounts"."GLAccountDescription", "tblGLAccountTypes"."Description", "tblGLPostings"."TransactionDate", "tblGLAccountHistory"."Amount", "tblGLAccountTypes"."HasZeroBalanceYearToYear", "tblGLAccounts"."GLAccountTypeID", "tblTrustee"."TrusteeName", "tblTrustee"."Chapter", "tblTrustee"."TrusteeID", ( SELECT COUNT(*)
FROM dbo.tblGLAccounts
WHERE dbo.tblGLAccounts.GLAccountTypeID = tblGLAccounts.GLAccountTypeID
AND dbo.tblGLAccounts.GLAccountNumber < tblGLAccounts.GLAccountNumber
AND dbo.tblGLAccounts.BreakPoint = 1 )
, ( SELECT COUNT(*)
FROM dbo.tblGLAccounts
WHERE dbo.tblGLAccounts.GLAccountTypeID = tblGLAccounts.GLAccountTypeID
AND dbo.tblGLAccounts.BreakPoint = 1 )
, ( SELECT TOP 1 dbo.tblGLPostings.TrusteeID
FROM dbo.tblGLPostings
ORDER BY dbo.tblGLPostings.TransactionDate DESC )
FROM ((("13Software"."dbo"."tblGLAccounts" "tblGLAccounts" LEFT OUTER JOIN "13Software"."dbo"."tblGLAccountHistory" "tblGLAccountHistory" ON "tblGLAccounts"."GLAccountID"="tblGLAccountHistory"."GLAccountID") INNER JOIN "13Software"."dbo"."tblGLAccountTypes" "tblGLAccountTypes" ON "tblGLAccounts"."GLAccountTypeID"="tblGLAccountTypes"."GLAccountTypeID") LEFT OUTER JOIN "13Software"."dbo"."tblGLPostings" "tblGLPostings" ON "tblGLAccountHistory"."GLPostingID"="tblGLPostings"."GLPostingID") LEFT OUTER JOIN "13Software"."dbo"."tblTrustee" "tblTrustee" ON "tblGLPostings"."TrusteeID"="tblTrustee"."TrusteeID"
WHERE ("tblGLAccounts"."GLAccountNumber">=0 AND "tblGLAccounts"."GLAccountNumber"<=999999) AND "tblTrustee"."TrusteeID"=( SELECT TOP 1 dbo.tblGLPostings.TrusteeID
FROM dbo.tblGLPostings
ORDER BY dbo.tblGLPostings.TransactionDate DESC ) AND ("tblGLPostings"."TransactionDate" IS NULL OR "tblGLPostings"."TransactionDate"<{ts '2011-02-01 00:00:00'})
ORDER BY "tblGLAccounts"."GLAccountTypeID", ( SELECT COUNT(*)
FROM dbo.tblGLAccounts
WHERE dbo.tblGLAccounts.GLAccountTypeID = tblGLAccounts.GLAccountTypeID
AND dbo.tblGLAccounts.GLAccountNumber < tblGLAccounts.GLAccountNumber
AND dbo.tblGLAccounts.BreakPoint = 1 )
, "tblGLAccounts"."GLAccountNumber", "tblGLPostings"."TransactionDate"
You can see the "( SELECT COUNT(*) " subqueries in the initial SELECT statement and in the ORDER BY statement.
If I change the alias of tblGLAccounts within the Database Expert to tblGLAccounts_1, the CR-generated SQL looks like:
SELECT "tblGLAccounts_1"."GLAccountNumber", "tblGLAccounts_1"."GLAccountDescription", "tblGLAccountTypes"."Description", "tblGLPostings"."TransactionDate", "tblGLAccountHistory"."Amount", "tblGLAccountTypes"."HasZeroBalanceYearToYear", "tblGLAccounts_1"."GLAccountTypeID", "tblTrustee"."TrusteeName", "tblTrustee"."Chapter", "tblTrustee"."TrusteeID", ( SELECT COUNT(*)
FROM dbo.tblGLAccounts
WHERE dbo.tblGLAccounts.GLAccountTypeID = tblGLAccounts_1.GLAccountTypeID
AND dbo.tblGLAccounts.GLAccountNumber < tblGLAccounts_1.GLAccountNumber
AND dbo.tblGLAccounts.BreakPoint = 1 )
, ( SELECT COUNT(*)
FROM dbo.tblGLAccounts
WHERE dbo.tblGLAccounts.GLAccountTypeID = tblGLAccounts_1.GLAccountTypeID
AND dbo.tblGLAccounts.BreakPoint = 1 )
, ( SELECT TOP 1 dbo.tblGLPostings.TrusteeID
FROM dbo.tblGLPostings
ORDER BY dbo.tblGLPostings.TransactionDate DESC )
FROM ((("13Software"."dbo"."tblGLAccounts" "tblGLAccounts_1" LEFT OUTER JOIN "13Software"."dbo"."tblGLAccountHistory" "tblGLAccountHistory" ON "tblGLAccounts_1"."GLAccountID"="tblGLAccountHistory"."GLAccountID") INNER JOIN "13Software"."dbo"."tblGLAccountTypes" "tblGLAccountTypes" ON "tblGLAccounts_1"."GLAccountTypeID"="tblGLAccountTypes"."GLAccountTypeID") LEFT OUTER JOIN "13Software"."dbo"."tblGLPostings" "tblGLPostings" ON "tblGLAccountHistory"."GLPostingID"="tblGLPostings"."GLPostingID") LEFT OUTER JOIN "13Software"."dbo"."tblTrustee" "tblTrustee" ON "tblGLPostings"."TrusteeID"="tblTrustee"."TrusteeID"
WHERE ("tblGLAccounts_1"."GLAccountNumber">=0 AND "tblGLAccounts_1"."GLAccountNumber"<=999999) AND "tblTrustee"."TrusteeID"=( SELECT TOP 1 dbo.tblGLPostings.TrusteeID
FROM dbo.tblGLPostings
ORDER BY dbo.tblGLPostings.TransactionDate DESC ) AND ("tblGLPostings"."TransactionDate" IS NULL OR "tblGLPostings"."TransactionDate"<{ts '2011-02-01 00:00:00'})
ORDER BY "tblGLAccounts_1"."GLAccountTypeID", ( SELECT COUNT(*)
FROM dbo.tblGLAccounts
WHERE dbo.tblGLAccounts.GLAccountTypeID = tblGLAccounts_1.GLAccountTypeID
AND dbo.tblGLAccounts.GLAccountNumber < tblGLAccounts_1.GLAccountNumber
AND dbo.tblGLAccounts.BreakPoint = 1 )
, "tblGLAccounts_1"."GLAccountNumber", "tblGLPostings"."TransactionDate"
There is no noticeable change in structure or syntax (other than the aliases), but this works in CR 2008 SP4 with SQL 2008 R2 when the other does not.
Ah, thanks for the reminders....
I don't see the difference between using a SQL Expression and a Command object? Cr simply passes the SQL Command as is, not sure what we do for Expressions...
I don't see the difference in Telling your users to use a Command and not a Expression? They still have to write something, or someone has to, they are exactly the same thing... Only issues if you use multiple Commands ( or data sources ) then you can't use SQL Expressions.
I'll ping the Database guys in R&D but I doubt we can do anything. There were a few issues which arose with Expressions and the order they are executed and verified that likely is now an issue for SQL Server R2. I don't believe we will change it now because it fixed issues that appears to have affected Expressions but because what you are doing is not correct they won't change it back. But I'll ask anyways.
Thanks again
Don
Thanks.
Maybe it is just how I understand how Commands work. If have used database expert and selected several tables, and then I add a command, I cannot relate the data generated by the command back to a specific table in the report unless I also include the table that the data should relate back to and the index field that it uses. In which case, the command slows the report way down as the data it generates is not indexed.
It seems to me that if you are going to use a command and you want to maintain optimal report performance, ALL data in the report should come from the command, not a combination of command and selected tables/views. Am I incorrect in this?
I apologize if I have gotten off-topic.
You are correct about the mixing of commands and other table objects. If you are going to use commands, 99% of the time you are better off getting everything into 1 single command.
As far as the issue at at hand... AS Don mentioned, I don't use SQL Expressions very often due to the fact that I prefer write my own SQL in commands. I have played around with them just so that I'm familiar with them...
I did do a quick test using CR XI R2 / SQL Server 2008 R2 / AdventureWorks... To count the number of orders for each contact.
Using Person.Contact as the main table referenced in the Database Expert and Sales.SalesOrderHeader in the SQL Expression.
1st Expression...
(SELECT COUNT(*) FROM Sales.SalesOrderHeader s WHERE Contact.ContactID = s.ContactID) AS OrderCount
the resulting SQL...
SELECT
"Contact"."ContactID",
"Contact"."FirstName",
"Contact"."LastName",
(SELECT COUNT(*) FROM Sales.SalesOrderHeader s WHERE Contact.ContactID = s.ContactID) AS OrderCount
FROM "AdventureWorks"."Person"."Contact" "Contact"
This worked as expected, returning the order count for each contact.
-
2nd Expression...
(SELECT COUNT(*) FROM Sales.SalesOrderHeader Contact WHERE Contact.ContactID = Contact.ContactID) AS OrderCount
the resulting SQL...
SELECT
"Contact"."ContactID",
"Contact"."FirstName",
"Contact"."LastName",
(SELECT COUNT(*) FROM Sales.SalesOrderHeader Contact WHERE Contact.ContactID = Contact.ContactID) AS OrderCount
FROM "AdventureWorks"."Person"."Contact" "Contact"
This version actually executed, but it returned the total order count for the entire SalesOrderHeader table for each contact.
So, while yes, it technically worked in CR XI R2, it returned incorrect results due to the fact that the 2 tables shared the same alias.
In summary, each table and each instance of a table should have a unique name or alias... No matter what version you are using.
HTH,
Jason
Interesting, I have never been able to get table aliases in a FROM clause in a SQL expression to work. Truth be told, if the alias for the table in the FROM clause matched the alias for another table in the report, I would expect vastly erroneous data to be returned. In my original example...
( SELECT COUNT(*)
FROM dbo.tblGLAccounts
WHERE dbo.tblGLAccounts.GLAccountTypeID = tblGLAccounts.GLAccountTypeID
AND dbo.tblGLAccounts.GLAccountNumber < tblGLAccounts.GLAccountNumber
AND dbo.tblGLAccounts.BreakPoint = 1 )
the table in the FROM clause does not have an alias. It is consistently referred to as "dbo.tblGLAccounts" while "tblGLAccounts" is the alias from Database Expert. When I try this:
( SELECT COUNT(*)
FROM dbo.tblGLAccounts Acct
WHERE Acct.GLAccountTypeID = tblGLAccounts.GLAccountTypeID
AND Acct.GLAccountNumber < tblGLAccounts.GLAccountNumber
AND Acct.BreakPoint = 1 )
I get the following error:
Error in compiling SQL Expression:
Failed to retrieve data from the database.
Details: ADO Error Code:0x80040e14
Source: Microsoft SQL Server Native Client 10.0
Description: The multi-part identifier "tblGLAccounts.GLAccountTypeID" could not be bound.
SQL State: 42522
Native Error: 4104
That is odd. The fact that It's throwing an error on the table name that's remained constant is a bit puzzling...
Also, in the absence of a specified schema name, SQL Server will always assume "dbo". So saying dbo.tblGLAccounts is the same as saying tblGLAccounts.
As a closer test I created another SQL Expression, this time correlating Person.Contact back to itself.
(SELECT COUNT(*) FROM Person.Contact c WHERE "Contact"."FirstName" = c.FirstName) AS FirstNameCount
Once again this worked just fine, returning a count of first names that match the contact's first name.
-
While this, on the other hand...
(SELECT COUNT(*) FROM Person.Contact Contact WHERE "Contact"."FirstName" = Contact.FirstName) AS FirstNameCount
-- and --
(SELECT COUNT(*) FROM Person.Contact WHERE "Contact"."FirstName" = Person.Contact.FirstName) AS FirstNameCount
...both returned the full table count for each contact.
-
I didn't get any errors in any of the 3 versions.
I can't see anything in there that looks like it would have an impact, but as you wish...
All of the following are checked:
Database Server is Case-Insensitive
Perform Grouping On Server
Use Indexes Or Server For Speed
Verify on First Refresh
Save Data With report
Create Group Tree
Display Alerts on Refresh
Select Distinct Data for Browsing
Prompt For Hyperlinks
The rest are unchecked...
Report Options that are checked:
Convert Database NULL Values to Default
Convert Other NULL Values to Default
Database Server is Case-Insensitive
Use Indexes Or Server For Speed
Verify on First Refresh
Show Previous Panel
Display Alerts on Refresh
Select Distinct Data for Browsing
Prompt for Hyperlinks
Hi guys,
Try setting your defaults to the same as Jason to see if that helps.
I'm thinking this may be a SQL Configuration setting now or possibly this:
http://msdn.microsoft.com/en-us/library/ms187455.aspx
Or this:
http://msdn.microsoft.com/en-us/library/ms175176.aspx
I did a google search on the error and found a bunch of posts with people using aliasing...
Thanks again
Don
I'm thinking that the issue is either an SQLNCLI driver issue or SQL Server user rights issue. I just trued this again using another SQL Server database and got the same error that Tromine is getting.
Here are the immediate differences...
Database1 Database2
DB Location Local Workstation Network Server
Version 2008R2 exp 2005 Standard
Rights SA Data Reader
Driver Native Client 10 Native Client
Connection Type OLEDB OLEDB
Remote Conn yes yes
Tromine said he's tested with both versions of the driver, so that's probably not it...
Local workstation vs network server shouldn't matter... but neither should SA vs Data Reader...
If it weren't for the fact that Tromine is also using 2008R2 I say it was difference between the db engines...
-
It looks to me like CR is trying to execute the the sub-query, independent of the rest of the query. If that's the case, then it would make sense... A correlated sub-query can't execute without the outer query.
That just doesn't explain why the behavior would be different between the two instances.
-
The unfortunate part... As Don mentioned, CR doesn't actually support the execution of select statement in SQL Expressions... So we can't even report it as a bug.
Hi Jason,
The SA account is disabled on 2008 by default I thought? I had to create a new sb account with full rights to all DB's.... So it may be a permission issue.
Under my sb account I gave the account full access and then for the DB user mappings it only has Public checked on....
But still doesn't explain why it works for you and not Trom... I still think it's a permission configuration setting though. CR will query the DB for the table names, odd that it has to use an alias to connect though but I have not played with 2008 much so I don't know all of the changes MS did to the Roles and security differences between 2005 and 2008. I do know they changed a lot...
Thanks
Don
Makes no sense to me...
I created two test reports, open at the same time, just on two different tabs. One pointing at the AdventureWorks db sitting on SQL Server 2008 R2 Express, running on my local machine. The second was pointing a production database sitting on SQL Server 2005, running on a network server.
The 1st one worked perfectly, the 2nd... not so much. It got the same error described above.
I be willing to bet that if Tromine were to download a copy of SQL Server Express (or Developer) to his local workstation it would work for him as well.
Jason
Jason, Are you using the SQL Native 10 driver for both SQL 2008 and 2005? I know we have seen differences when using the Native driver on 2008 as compared to the Native driver, you must use the Native 10 driver for 2008 but I have not seen problems using the Native 10 with 2005. But I don't use SQL Expressions with SELECT in them either....
Anyways, this is something we can NOT "fix". Just don't do it or if you must you'll have to use the Alias option until something changes again next time in CR or the Db Client....
Thanks again for hashing this out.
Don
Edited by: Don Williams on Jul 18, 2011 9:01 AM
The inability to use aliases in CR sql expressions has been the norm for me through all combinations of CR (10 Adv. Dev, XI, XI R2, 2008) and SQL Server (2000, 2005, 2008, 2008 R2). I can play around with how the reporting user is set up in SQL and see if that changes it.
It appears to be an issue with how the various versions of SQL interpret subtle changes in the naming of instances of a table. I took the output from "Show SQL Query..." from my original report and ran it in both SQL 2008 and SQL 2008 R2. The resulting data sets were different.
The set from SQL 2008 was consistent with how I wanted and expected it to work.
The SQL 2008 R2 set was consistent with the problem that I reported.
It appears that how CR interfaces with SQL 2008 R2 is not the issue - it's how SQL 2008 R2 is interpreting the addition of the schema to the table name in the sub-queries. Somehow, I missed this in all the trouble-shooting I did before posting in the forum.
In any case, this does not seem to be a CR issue. There may be a setting in SQL Server that will resolve this.
OK, well, in the course of reviewing SQL settings, I noticed that the database I was using in SQL Server 2008 had it's compatibility mode set to "SQL 2000". Changed it to "SQL 2008" and now the report's generated SQL query returns identical wrong data sets on both servers. And as Jason pointed out, the dataset is not really wrong - SQL interprets "dbo.tblGLAccounts" and "tblGLAccounts" the same way in the sub-query. Evidently, SQL 2000 did not interpret these the same way. Looks like this interpretation method may have changed in SQL 2005.
Without polling all our customers, I would have to guess that most customers are still using SQL 2000 compatibility mode in their SQL Server 2005, 2008, and 2008 R2 installations.
I'm going to go crawl back under my rock and bang my head against the wall for a while.
Thanks for your help, guys. This is something I am just going to have to adapt to and work around.
hello all,
i too use sql expressions with selects all the time. i don't think that it's possible to tell all customers to use a command as that would entail redesigning every report they have. setting multiple tables to a single command object is difficult if not impossible as well.
i have seen this error in the past and what i do to try to get around it is
A) - trying to set the full path to the tables (using dbo....tablename) or removing dbo...looks like you've tried that though
here's the one that works the most often to get rid of this error
B) - aliasing the table in the sql expression to a name which is different than the table name in the main report
- now bring the main report's table into the sql expression's WHERE clause making something like
maintable.id = aliastable.id
C) the other workaround for counts uses a formula instead of a sql expression field...this will only work of course if you have the applicable tables on the main report. otherwise a sql expression is needed.
if AccountTypeID = GLAccountTypeID
AND GLAccountNumber < GLAccountNumber
AND BreakPoint = 1
then 1 else 0
now put that new formula on the report and sum it.
i am hoping that method B above will work for you though.
jamie
Thanks, Jamie.
Method B has never worked for me - I always get an error (it's listed in one of my posts in this thread if you want to find it.) No idea why. Jason and Don have been trying to figure it out.
However, as an alternative to Method B, I can change the alias of the table in the report in the Database Expert and then use that reference in the SQL expression.
Hi Guys,
Jamie and I have had discussions with our PM group and Database team and they have indicated that we did make a change, all tables are aliased now which does confirm what you are seeing.
Officially we do NOT support Select Statements but Jamie has a great internal business case also as well as this post and others Jason has been on...
Once we get a decision with what we are proposing and what R&D is willing to do I'll update this post.
Thanks again
Don
Ha Ha... doesn't hurt to ask... Add your request to Idea Place, the PM's monitor that site for enhancement requests daily.
They are only looking at this current problem as possibly a regression ( legacy functionality ) issue. They will not be adding anything new to CR for Windows. Possibly, and only a slight possibility, they may officially add New SQL Expression capabilities and functionality to CR for Enterprise, it's the latest Java only version of the report Designer. It will not be added to the Windows version of Crystal Reports. It's only been suggested to add it a wish list right now and may be a few versions before it actually is added if at all....
Thanks again
Don
User | Count |
---|---|
67 | |
12 | |
12 | |
7 | |
7 | |
4 | |
4 | |
4 | |
4 | |
3 |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.