SSMS Error "An expression of non-boolean type specified in a context where a condition is expected, near '('" - sql-server

I am getting the titled error from a number of SQL Server views I am trying to create. They are modified from a MS Access database I'm upgrading to use SQL as the back end. See SQL below:
SELECT dbo.Site_Info_All.SiteID,
IIf(dbo.Site_Info_All.Fixed_Charge, N'Yes', N'No') AS [Fixed Charge],
dbo.Site_Info_All.Fixed_Charge_Date, dbo.MG_Definition.MG_Definition
FROM dbo.MG_Definition INNER JOIN
dbo.Site_Info_All ON dbo.MG_Definition.MG_DefinitionID =
dbo.Site_Info_All.MG_DefinitionID
It looks as though I'm being told that the Fixed_Charge field is not boolean, except that it is. I'm encountering this issue with multiple views. What am I doing wrong?

It's not boolean. It's a bit. :)
A bit column doesn't directly evaluate to true or false, the way a bool does in other languages. You actually have to compare it to another bit value to return the boolean value.
declare #bit bit = 1;
if (#bit) print '#bit was true'; -- this does not work
if (#bit = 1) print '#bit = 1 was true'; -- this works
What you want is:
... IIf(dbo.Site_Info_All.Fixed_Charge = 1, N'Yes', N'No') ...
SELECT dbo.Site_Info_All.SiteID,
IIf(dbo.Site_Info_All.Fixed_Charge = 1, N'Yes', N'No') AS [Fixed Charge],
dbo.Site_Info_All.Fixed_Charge_Date,
dbo.MG_Definition.MG_Definition
FROM dbo.MG_Definition
INNER JOIN dbo.Site_Info_All ON dbo.MG_Definition.MG_DefinitionID = dbo.Site_Info_All.MG_DefinitionID

In the end, I decided that IIF simply wasn't working for whatever reason. An answer to that would be very welcome, btw.
I used
SELECT dbo.Site_Info_All.SiteID,
CASE WHEN dbo.Site_Info_All.Fixed_Charge = 1
THEN N'Yes'
ELSE N'No'
END AS [Fixed Charge], dbo.Site_Info_All.Fixed_Charge_Date,
dbo.MG_Definition.MG_Definition
FROM dbo.MG_Definition INNER JOIN
dbo.Site_Info_All ON dbo.MG_Definition.MG_DefinitionID =
dbo.Site_Info_All.MG_DefinitionID
That gave me the results I was after, but only after pulling out what little hair I have left.

Related

Yet another subquery issue

Hello from an absolute beginner in SQL!
I have a field I want to populate based on another table. For this I have written this query, which fails with: Msg 512, Level 16, State 1, Line 1
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
The statement has been terminated.
oK, here goes:
Update kre.CustomerOrderLineCopy
SET DepNo = (SELECT customerordercopy.DepNo
FROM kre.CustomerOrderCopy , kre.CustomerOrderLineCopy
WHERE CustomerOrderLineCopy.OrderCopyNo =kre.CustomerOrderCopy.OrderCopyNo)
WHERE CustomerOrderLineCopy.OrderCopyNo = (SELECT CustomerOrderCopy.OrderCopyNo
FROM kre.CustomerOrderCopy, kre.CustomerOrderLineCopy
WHERE kre.CustomerOrderLineCopy.OrderCopyNo = kre.CustomerOrderCopy.OrderCopyNo)
What I'm trying to do is to change DepNo in CustomerOrderLineCopy, with the value in DepNo in CustomerOrderCopy - based on the same OrderCopyNo in both tables.
I'm open for all suggestion.
Thanks,
ohalvors
If you just join the tables together the update is easier:
UPDATE A SET A.DepNo = B.DepNo
FROM kre.CustomerOrderLineCopy A
INNER JOIN kre.CustomerOrderCopy B ON A.OrderCopyNo = B.OrderCopyNo
The problem is that at least one of your sub queries return more than one value. Think about this:
tablePerson(name, age)
Adam, 11
Eva, 11
Sven 22
update tablePerson
set name = (select name from tablePerson where age = 11)
where name = 'Sven'
Which is equivalent to: set Sven's name to Adam and Eva. Which is not possible.
If you want to use sub queries, either make sure your sub queries can only return one value or force one value by using:
select top 1 xxx from ...
This may be enough to quieten it down:
Update kre.CustomerOrderLineCopy
SET DepNo = (SELECT customerordercopy.DepNo
FROM kre.CustomerOrderCopy --, kre.CustomerOrderLineCopy
WHERE CustomerOrderLineCopy.OrderCopyNo =kre.CustomerOrderCopy.OrderCopyNo)
WHERE CustomerOrderLineCopy.OrderCopyNo = (SELECT CustomerOrderCopy.OrderCopyNo
FROM kre.CustomerOrderCopy --, kre.CustomerOrderLineCopy
WHERE kre.CustomerOrderLineCopy.OrderCopyNo = kre.CustomerOrderCopy.OrderCopyNo)
(Where I've commented out kre.CustomerOrderLineCopy in the subqueries) That is, you were hopefully trying to correlate these subqueries with the outer table - not introduce another instance of kre.CustomerOrderLineCopy.
If you still get an error, then you still have multiple rows in kre.CustomerOrderCopy which have the same OrderCopyNo. If that's so, you need to give us (and SQL Server) the rules that you want to apply for how to select which row you want to use.
The danger of switching to the FROM ... JOIN form shown in #Avitus's answer is that it will no longer report if there are multiple matching rows - it will just silently pick one of them - which one is never made clear.
Now I look at the query again, I'm not sure it even needs a WHERE clause now. I think this is the same:
Update kre.CustomerOrderLineCopy
SET DepNo = (
SELECT customerordercopy.DepNo
FROM kre.CustomerOrderCopy
WHERE CustomerOrderLineCopy.OrderCopyNo = kre.CustomerOrderCopy.OrderCopyNo)

Query with integers not working

I've been searching here on stackoverflow and other sources but not found a solution to this
The query below works as expected expect for when either custinfo.cust_cntct_id or custinfo.cust_corrcntct_id = '' (blank not NULL) then I get no results. Both are integer fields and if both have an integer value then I get results. I still want a value returned for either cntct_email or corrcntct_email even if custinfo.cust_cntct_id or custinfo.cust_corrcntct_id = blank
Can someone help me out in making this work? The database is PostgreSQL.
SELECT
cntct.cntct_email AS cntct_email,
corrcntct.cntct_email AS corrcntct_email
FROM
public.custinfo,
public.invchead,
public.cntct,
public.cntct corrcntct
WHERE
invchead.invchead_cust_id = custinfo.cust_id AND
cntct.cntct_id = custinfo.cust_cntct_id AND
corrcntct.cntct_id = custinfo.cust_corrcntct_id;
PostgreSQL won't actually let you test an integer field for a blank value (unless you're using a truly ancient version - 8.2 or older), so you must be using a query generator that's "helpfully" transforming '' to NULL or a tool that's ignoring errors.
Observe this, on Pg 9.2:
regress=> CREATE TABLE test ( a integer );
CREATE TABLE
regress=> insert into test (a) values (1),(2),(3);
INSERT 0 3
regress=> SELECT a FROM test WHERE a = '';
ERROR: invalid input syntax for integer: ""
LINE 1: SELECT a FROM test WHERE a = '';
If you are attempting to test for = NULL, this is not correct. You must use IS NOT NULL or IS DISTINCT FROM NULL instead. Testing for = NULL always results in NULL, which is treated as false in a WHERE clause.
Example:
regress=> insert into test (a) values (null);
INSERT 0 1
regress=> SELECT a FROM test WHERE a = NULL;
a
---
(0 rows)
regress=> SELECT a FROM test WHERE a IS NULL;
a
---
(1 row)
regress=> SELECT NULL = NULL as wrong, NULL IS NULL AS right;
wrong | right
-------+-------
| t
(1 row)
By the way, you should really be using ANSI JOIN syntax. It's more readable and it's much easier to forget to put a condition in and get a cartesian product by accident. I'd rewrite your query for identical functionality and performance but better readability as:
SELECT
cntct.cntct_email AS cntct_email,
corrcntct.cntct_email AS corrcntct_email
FROM
public.custinfo ci
INNER JOIN public.invchead
ON (invchead.invchead_cust_id = ci.cust_id)
INNER JOIN public.cntct
ON (cntct.cntct_id = ci.cust_cntct_id)
INNER JOIN public.cntct corrcntct
ON (corrcntct.cntct_id = ci.cust_corrcntct_id);
Use of table aliases usually keeps it cleaner; here I've aliased the longer name custinfo to ci for brevity.

Using a function in a query that returns a string or a null

I want to join 2 tables 'addresses' and 'user_info' on user_id and app_id
(which is a number, or it is null), like these 2 examples:
select * from user_info
left outer join addresses on addresses.user_id = user_info.user_id
and addresses.app_id is null
select * from user_info
left outer join addresses on addresses.user_id = user_info.user_id
and addresses.app_id = 1234
What the app_id should be is complicated and I have written a function to return it. It returns a string, which would be for example "is null" or "= 1234".
I'm trying to call it with this syntax:
select * from user_info
left outer join addresses on addresses.user_id = user_info.user_id
and addresses.app_id dbo.fnGetAppId(addresses.user_id)
I get this error:
Msg 4145, Level 15, State 1, Line 3 An
expression of non-boolean type
specified in a context where a
condition is expected, near 'dbo'.
I'd like to keep the query very simple as it is without having to determine if the function is returning a null or not.
Can you suggest the best way to keep the calling query very simple?
(I'm in sql server 2005.)
NULL != NULL. If either address.app_id = NULL or fnGetAppID = NULL, the comparison will fail. I would write the comparison as:
coalesce(address.app_id, 'NULLSTRING') = coalesce(dbo.fnGetAppID(addresses.user_id), 'NULLSTRING')
It looks like you're just missing an = sign
addresses.app_id dbo.fnGetAppId(addresses.user_id)
rather than
addresses.app_id = dbo.fnGetAppId(addresses.user_id)
So if fnGetAppId is null then this query looks like the following?
select * from user_info left outer join addresses on addresses.user_id = user_info.user_id and null
I doubt that is what you want. :)
You may want to do a simple check in your logic, before calling the query, to properly handle a null for fnGetAppId and as Clyde mentioned you also need an = sign for a non-null
.
As James Black pointed out, you have AND where you probably want WHERE. Beyond this, I suggest you make the function a boolean one (passing address.app_id to it as one more argument), so it can perform an IS NULL or = 1234 as appropriate (Bill's COALESCE solution is clever indeed, but putting the appropriate comparison inside the function is more straightforward, IMO).

How to Filter variables for SP, to return only one value from many in SQL View

In a SQL View, I have 5 boolean variables and one int variable.
out of 5 boolean variables only one variable will be true for a single data row,
Task Type boolVerySmall Datestart TagName
Architecture Setup -- Doc Code True 1900-01-01 00:00:00.000 Design_09
idProject boolsmall boolMedium boolLarge boolVeryLarge intHours
4 False False False False 0
The above data is for one row... in this row, when I download the data from database to excel sheet, I have to display only the value which is true (boolVerySmall ,boolsmall, boolMedium,boolLarge, boolVeryLarge, intHours) should be displayed in a single column,
I have written a Stored procedure for this. I am finding it difficult to get a particular row which has TRUE of intHours>0.
I am adding the sql query below. Please help me
SELECT dbo.tblResourceTaskList.txtTask, dbo.tblIndividualRelativeData.txtProductType, dbo.tblResourceTaskList.boolVerySmall,
dbo.tblResourceTaskList.dtActualCompletionDate, dbo.tblEffortCodes.txtTagName, dbo.tblResourceTaskList.txtTaskNotes, dbo.tblResourceTaskList.idSubProject, dbo.tblResourceTaskList.idLaunch, dbo.tblResourceTaskList.idResource, dbo.tblResourceTaskList.boolSmall, dbo.tblResourceTaskList.boolMedium, dbo.tblResourceTaskList.boolLarge, dbo.tblResourceTaskList.boolVeryLarge,
dbo.tblResourceTaskList.intDirectHours
FROM dbo.tblResourceTaskList INNER JOIN dbo.tblProjectUsers ON dbo.tblResourceTaskList.idSubProject = dbo.tblProjectUsers.idSubProject AND
dbo.tblResourceTaskList.idResource = dbo.tblProjectUsers.idUser INNER JOIN
dbo.tblLaunchInfo ON dbo.tblResourceTaskList.idLaunch = dbo.tblLaunchInfo.idLaunch INNER JOIN dbo.tblIndividualRelativeData ON dbo.tblResourceTaskList.idIndividualRelativeEffort = dbo.tblIndividualRelativeData.idIndividualRelativeEffort LEFT OUTER JOINdbo.tblEffortCodes ON dbo.tblResourceTaskList.idEffortCode = dbo.tblEffortCodes.idEffortCode
My stored procedure is
SELECT txtTask, txtProductType, boolVerySmall, txtTagName, txtTaskNotes,
dtActualCompletionDate
FROM tblResourceTaskList_View
WHERE (idSubProject = #idSubProjectIndex )
as the first row from db has TRUE for boolVerySmall, then SP should return this value and if its other than that it should give that value.
Instead of filling True in excel sheet, I , have to assign VS for boolVerySmall, S - Small, M - Medium, L - Large VL- VeryLarge in the excelsheet.
So please help me how to work on this.
1. Assigning shortnames (like VS... for the bool var's).
2. Returning only one value (boolverysmall or ... ) and assigning VS to that and fill it in excel.
3. if all the bools are false, then intHours should be assinged...
Kindly help me.
Thanks
Ramm
I am able to assign values to SP using CASE statements.
Now, its workign fine.
Thanks
Ramm

LINQ to SQL Take w/o Skip Causes Multiple SQL Statements

I have a LINQ to SQL query:
from at in Context.Transaction
select new {
at.Amount,
at.PostingDate,
Details =
from tb in at.TransactionDetail
select new {
Amount = tb.Amount,
Description = tb.Desc
}
}
This results in one SQL statement being executed. All is good.
However, if I attempt to return known types from this query, even if they have the same structure as the anonymous types, I get one SQL statement executed for the top level and then an additional SQL statement for each "child" set.
Is there any way to get LINQ to SQL to issue one SQL statement and use known types?
EDIT: I must have another issue. When I plugged a very simplistic (but still hieararchical) version of my query into LINQPad and used freshly created known types with just 2 or 3 members, I did get one SQL statement. I will post and update when I know more.
EDIT 2: This appears to be due to a bug in Take. See my answer below for details.
First - some reasoning for the Take bug.
If you just Take, the query translator just uses top. Top10 will not give the right answer if cardinality is broken by joining in a child collection. So the query translator doesn't join in the child collection (instead it requeries for the children).
If you Skip and Take, then the query translator kicks in with some RowNumber logic over the parent rows... these rownumbers let it take 10 parents, even if that's really 50 records due to each parent having 5 children.
If you Skip(0) and Take, Skip is removed as a non-operation by the translator - it's just like you never said Skip.
This is going to be a hard conceptual leap to from where you are (calling Skip and Take) to a "simple workaround". What we need to do - is force the translation to occur at a point where the translator can't remove Skip(0) as a non-operation. We need to call Skip, and supply the skipped number at a later point.
DataClasses1DataContext myDC = new DataClasses1DataContext();
//setting up log so we can see what's going on
myDC.Log = Console.Out;
//hierarchical query - not important
var query = myDC.Options.Select(option => new{
ID = option.ParentID,
Others = myDC.Options.Select(option2 => new{
ID = option2.ParentID
})
});
//request translation of the query! Important!
var compQuery = System.Data.Linq.CompiledQuery
.Compile<DataClasses1DataContext, int, int, System.Collections.IEnumerable>
( (dc, skip, take) => query.Skip(skip).Take(take) );
//now run the query and specify that 0 rows are to be skipped.
compQuery.Invoke(myDC, 0, 10);
This produces the following query:
SELECT [t1].[ParentID], [t2].[ParentID] AS [ParentID2], (
SELECT COUNT(*)
FROM [dbo].[Option] AS [t3]
) AS [value]
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY [t0].[ID]) AS [ROW_NUMBER], [t0].[ParentID]
FROM [dbo].[Option] AS [t0]
) AS [t1]
LEFT OUTER JOIN [dbo].[Option] AS [t2] ON 1=1
WHERE [t1].[ROW_NUMBER] BETWEEN #p0 + 1 AND #p1 + #p2
ORDER BY [t1].[ROW_NUMBER], [t2].[ID]
-- #p0: Input Int (Size = 0; Prec = 0; Scale = 0) [0]
-- #p1: Input Int (Size = 0; Prec = 0; Scale = 0) [0]
-- #p2: Input Int (Size = 0; Prec = 0; Scale = 0) [10]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1
And here's where we win!
WHERE [t1].[ROW_NUMBER] BETWEEN #p0 + 1 AND #p1 + #p2
I've now determined this is the result of a horrible bug. The anonymous versus known type turned out not to be the cause. The real cause is Take.
The following result in 1 SQL statement:
query.Skip(1).Take(10).ToList();
query.ToList();
However, the following exhibit the one sql statement per parent row problem.
query.Skip(0).Take(10).ToList();
query.Take(10).ToList();
Can anyone think of any simple workarounds for this?
EDIT: The only workaround I've come up with is to check to see if I'm on the first page (IE Skip(0)) and then make two calls, one with Take(1) and the other with Skip(1).Take(pageSize - 1) and addRange the lists together.
I've not had a chance to try this but given that the anonymous type isn't part of LINQ rather a C# construct I wonder if you could use:
from at in Context.Transaction
select new KnownType(
at.Amount,
at.PostingDate,
Details =
from tb in at.TransactionDetail
select KnownSubType(
Amount = tb.Amount,
Description = tb.Desc
)
}
Obviously Details would need to be an IEnumerable collection.
I could be miles wide on this but it might at least give you a new line of thought to pursue which can't hurt so please excuse my rambling.

Resources