is it possible to select EXISTS directly as a bit? - sql

I was wondering if it's possible to do something like this (which doesn't work):
select cast( (exists(select * from theTable where theColumn like 'theValue%') as bit)
Seems like it should be doable, but lots of things that should work in SQL don't ;) I've seen workarounds for this (SELECT 1 where... Exists...) but it seems like I should be able to just cast the result of the exists function as a bit and be done with it.

No, you'll have to use a workaround.
If you must return a conditional bit 0/1 another way is to:
SELECT CAST(
CASE WHEN EXISTS(SELECT * FROM theTable where theColumn like 'theValue%') THEN 1
ELSE 0
END
AS BIT)
Or without the cast:
SELECT
CASE
WHEN EXISTS( SELECT 1 FROM theTable WHERE theColumn LIKE 'theValue%' )
THEN 1
ELSE 0
END

SELECT CAST(COUNT(*) AS bit) FROM MyTable WHERE theColumn like 'theValue%'
When you cast to bit
0 -> 0
everything else -> 1
And NULL -> NULL of course, but you can't get NULL with COUNT(*) without a GROUP BY
bit maps directly to boolean in .net datatypes, even if it isn't really...
This looks similar but gives no row (not zero) if no matches, so it's not the same
SELECT TOP 1 CAST(NumberKeyCOlumn AS bit) FROM MyTable WHERE theColumn like 'theValue%'

You can use IIF and CAST
SELECT CAST(IIF(EXISTS(SELECT * FROM theTable
where theColumn like 'theValue%'), 1, 0) AS BIT)

I'm a bit late on the uptake for this; just stumbled across the post. However here's a solution which is more efficient & neat than the selected answer, but should give the same functionality:
declare #t table (name nvarchar(16))
declare #b bit
insert #t select N'Simon Byorg' union select N'Roe Bott'
select #b = isnull((select top 1 1 from #t where name = N'Simon Byorg'),0)
select #b whenTrue
select #b = isnull((select top 1 1 from #t where name = N'Anne Droid'),0)
select #b whenFalse

You can also do the following:
SELECT DISTINCT 1
FROM theTable
WHERE theColumn LIKE 'theValue%'
If there are no values starting with 'theValue' this will return null (no records) rather than a bit 0 though

SELECT IIF(EXISTS(SELECT * FROM theTable WHERE theColumn LIKE 'theValue%'), 1, 0)

No it isn't possible. The bit data type is not a boolean data type. It is an integer data type that can be 0,1, or NULL.

Another solution is to use ISNULL in tandem with SELECT TOP 1 1:
SELECT ISNULL((SELECT TOP 1 1 FROM theTable where theColumn like 'theValue%'), 0)

I believe exists can only be used in a where clause, so you'll have to do a workaround (or a subquery with exists as the where clause). I don't know if that counts as a workaround.
What about this:
create table table1 (col1 int null)
go
select 'no items',CONVERT(bit, (select COUNT(*) from table1) ) -- returns 'no items', 0
go
insert into table1 (col1) values (1)
go
select '1 item',CONVERT(bit, (select COUNT(*) from table1) ) --returns '1 item', 1
go
insert into table1 (col1) values (2)
go
select '2 items',CONVERT(bit, (select COUNT(*) from table1) ) --returns '2 items', 1
go
insert into table1 (col1) values (3)
go
drop table table1
go

Related

Conditionally modify query based on parameter

I have this query (something like a case statement which I can use and fix it)
select *
from mytable
where 1=1
and (isNull(ID, 0) = 0 OR UtilityID IN (9,40))
I also want to add another statement
select *
from mytable
where 1=1
and UtilityID NOT IN (9,40)
Everything is happening in a procedure, so want to use a variable like declare #something so if that is passed as 1, use the first statement and the if 0 is passed, use the latter one.
While I appreciate the genius in Dale's answer I find this more readable:
IF #something = 0
BEGIN
select *
from mytable
where ID IS NULL OR ID = 0 OR UtilityID IN (9,40);
END
IF #something = 1
BEGIN
select *
from mytable
where UtilityID NOT IN (9,40);
END
It's procedure code, so use IF to direct the control flow. Also expanded and simplified your where clauses
I think I understand your logic, ignoring the 1=1 (which does nothing) you want to only allow id = 0 when #something = 1. This should do it:
declare #something bit = 0;
declare #mytable table (ID int, UtilityID int);
insert into #mytable (ID, UtilityID)
select 0, 1 union all
select 1, 2 union all
select 2, 9 union all
select 3, 40;
select *
from #mytable
where (
(#something = 1 and (isnull(ID, 0) = 0 or UtilityID in (9,40)))
or (#something = 0 and (UtilityID not in (9,40)))
);
A more performant approach for a larger query could be:
select *
from #mytable
where (#something = 1 and (isnull(ID, 0) = 0 or UtilityID in (9,40)))
union all
select *
from #mytable
where (#something = 0 and (UtilityID not in (9,40)));
PS: Hopefully your ID cannot ever by null - it should have a constraint on it.

Writing CASE Statement in SQL

I have a requirement to display two columns in a report,whose values will be determined using the same input expression as following:
SELECT
CASE WHEN id>10
THEN 'AAAA'
ELSE
'BBBB'
END as 'FirstColumn',
CASE WHEN id>10
THEN
'BBBB'
ELSE
'DDDD' END as 'SecondColumn'
Can I construct this expression without repeating input expression twice as they are same?
You will need to repeat the CASE statment for each field as while the condition might be the same teh results are differnt. The only other alternative is to use a UNion all statement whenre the first select takes the records WHERE id<=10 and the other one does the WHERE ID>10. This can bea viable alternative but it is a littel hareder to maintain, so if the performance is good enough, Iwoudl stick to repeating teh CASE condition.
If some sophisticated is supposed instead of id>10 then, to make it a bit shorter and a bit more readable:
select
IIF(p.b=1, 'AAA', 'BBB') [Col1],
IIF(p.b=1, 'BBB', 'DDD') [Col2]
from
TableName t
outer apply (select cast(case when t.id>10 then 1 else NULL end as bit) as b) p
However, this is only available in SqlServer 2012. In earlier versions, parhaps, you will have to write your own IIF-like scalar function:
create function dbo.IIF (#b bit, #ifValue varchar(50), #elseValue varchar(50))
returns varchar(50)
as begin
return (select case when #b = 1 then #ifValue else #elseValue end)
end
GO
select
dbo.IIF(p.b, 'AAA', 'BBB') [Col1],
dbo.IIF(p.b, 'BBB', 'DDD') [Col2]
from
TableName t
outer apply (select cast(case when t.id>10 then 1 else NULL end as bit) as b) p
If it's worth the trouble, you could created a User-defined function (UDF) that would do this transforamation logic:
CREATE FUNCTION dbo.fColTransform(#id Int)
RETURNS Varchar(20)
AS BEGIN
DECLARE #ret Varchar(20)
SET #ret =
CASE
WHEN #id IS NULL THEN 'Unknown'
WHEN #id > 10 THEN 'BBB'
ELSE 'DDD'
END
RETURN #ret
END
Then, in your SELECT, you could structure it like this:
SELECT dbo.fColTransform(id1) AS FirstColumn,
dbo.fColTransform(id2) AS SecondColumn
FROM MyTable
You can store it in a variable and use Execute to call it:
Declare #foo as nvarchar(max)
set #foo='MyTable'
execute ('Select * from ' + #foo)
The downside is that your entire query would be all red (since it's now a string).
Try with this query, maybe will be useful (in the case if you do not use a function):
WITH temp AS (
SELECT
CASE WHEN x.ID > 10
THEN 'AAAA'
ELSE 'BBBB'
END as Col
FROM (
SELECT 1 AS ID
UNION
SELECT 11 AS ID
UNION
SELECT 13 AS ID
UNION
SELECT 9 AS ID
UNION
SELECT 7 AS ID
) x
)
SELECT temp.Col AS FirstColumn, temp.Col AS SecondColumn
FROM temp
Basically:
With name As (
/*yourquery with one CASE-COLUMN*/
)
SELECT name.COL AS COLUMN1, name.COL AS COLUMN2 FROM name
You can try this here

Select with IN and Like

I have a very interesting problem. I have an SSRS report with a multiple select drop down.
The drop down allows to select more than one value, or all values.
All values is not the problem.
The problem is 1 or the combination of more than 1 option
When I select in the drop down 'AAA' it should return 3 values: 'AAA','AAA 1','AAA 2'
Right now is only returning 1 value.
QUESTION:
How can make the IN statement work like a LIKE?
The Drop down select
SELECT '(All)' AS team, '(All)' AS Descr
UNION ALL
SELECT 'AAA' , 'AAA'
UNION ALL
SELECT 'BBB' , 'BBB'
Table Mytable
ColumnA Varchar(5)
Values for ColumnA
'AAA'
'AAA 1'
'AAA 2'
'BBB'
'BBB 1'
'BBB 2'
SELECT * FROM Mytable
WHERE ColumnA IN (SELECT * FROM SplitListString(#Team, ',')))
Split function
CREATE FUNCTION [dbo].[SplitListString]
(#InputString NVARCHAR(max), #SplitChar CHAR(1))
RETURNS #ValuesList TABLE
(
param NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #ListValue NVARCHAR(max)
DECLARE #TmpString NVARCHAR(max)
DECLARE #PosSeparator INT
DECLARE #EndValues BIT
SET #TmpString = LTRIM(RTRIM(#InputString));
SET #EndValues = 0
WHILE (#EndValues = 0) BEGIN
SET #PosSeparator = CHARINDEX(#SplitChar, #TmpString)
IF (#PosSeparator) > 1 BEGIN
SELECT #ListValue = LTRIM(RTRIM(SUBSTRING(#TmpString, 1, #PosSeparator -1 )))
END
ELSE BEGIN
SELECT #ListValue = LTRIM(RTRIM(#TmpString))
SET #EndValues = 1
END
IF LEN(#ListValue) > 0 BEGIN
INSERT INTO #ValuesList
SELECT #ListValue
END
SET #TmpString = LTRIM(RTRIM(SUBSTRING(#TmpString, #PosSeparator + 1, LEN(#TmpString) - #PosSeparator)))
END
RETURN
END
You can't. But, you can make the like work like the like:
select *
from mytable t join
SplitListString(#Team, ',') s
on t.ColumnA like '%'+s.param+'%'
That is, move the split list to an explicit join. Replace with the actual column name returned by the function, and use the like function.
Or, if you prefer:
select *
from mytable t cross join
SplitListString(#Team, ',') s
where t.ColumnA like '%'+s.param+'%'
The two versions are equivalent and should produce the same execution plan.
Better approach would be to have a TeamsTable (teamID, teamName, ...) and teamMembersTable (teamMemberID, teamID, teamMemberDetails, ...).
Then you an build your dropdown list as
SELECT ... FROM TeamsTable ...;
and
SELECT ... FROM teamMembersTable WHERE teamID IN (valueFromYourDropDown);
Or you can just store your teamID or teamName (or both) in your (equivalent of) teamMembersTable
You're not going to get IN to work the same as LIKE without a lot of work. You could do something like this though (and it would be nice to see some of your actual data though so we could give better solutions):
SELECT *
FROM table
WHERE LEFT(field,3) IN #Parameter
If you'd like better performance, create a code field on your table and update it like this:
UPDATE table
SET codeField = LEFT(field,3)
Then just add an index on that field and run this query to get your results:
SELECT *
FROM table
WHERE codeField IN #Parameter

DELETE EXCEPT TOP 1

Is there any way to delete all the rows in a table except one (random) row, without specifying any column names in the DELETE statement?
I'm trying to do something like this:
CREATE TABLE [dbo].[DeleteExceptTop1]([Id] INT)
INSERT [dbo].[DeleteExceptTop1] SELECT 1
INSERT [dbo].[DeleteExceptTop1] SELECT 2
INSERT [dbo].[DeleteExceptTop1] SELECT 3
SELECT * FROM [dbo].[DeleteExceptTop1]
DELETE
FROM [dbo].[DeleteExceptTop1]
EXCEPT
SELECT TOP 1 * FROM [dbo].[DeleteExceptTop1]
SELECT * FROM [dbo].[DeleteExceptTop1]
The final SELECT should yield one row (could be any of the three).
;WITH CTE AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT newid())) AS RN
FROM [dbo].[DeleteExceptTop1]
)
DELETE FROM CTE
WHERE RN > 1
Or similar to #abatishchev's answer but with more variety in the ordering and avoiding deprecated constructs.
DECLARE #C INT
SELECT #C = COUNT(*) - 1
FROM [dbo].[DeleteExceptTop1]
IF #c > 0
BEGIN
WITH CTE AS
(
SELECT TOP(#C) *
FROM [dbo].[DeleteExceptTop1]
ORDER BY NEWID()
)
DELETE FROM CTE;
END
Or a final way that uses EXCEPT and assumes no duplicate rows and that all columns are of datatypes compatible with the EXCEPT operator
/*Materialise TOP 1 to ensure only evaluated once*/
SELECT TOP(1) *
INTO #T
FROM [dbo].[DeleteExceptTop1]
ORDER BY NEWID()
;WITH CTE AS
(
SELECT *
FROM [dbo].[DeleteExceptTop1] T1
WHERE EXISTS(
SELECT *
FROM #T
EXCEPT
SELECT T1.*)
)
DELETE FROM CTE;
DROP TABLE #T
Try:
declare #c int
select #c = count(*) - 1 from [dbo].[DeleteExceptTop1]
IF #c > 0
BEGIN
set RowCount #c
delete from [dbo].[DeleteExceptTop1]
END
No.
You need to use a column name (such as that of the primary key) to identify which rows you want to remove.
"random row" has no meaning in SQL except its data. If you want to delete everything except some row, you must differentiate that row from the others you with to DELETE
EXCEPT works by comparing the DISTINCT values in the row.
EDIT: If you can specify the primary key then this is a trivial matter. You can simply DELETE where the PK <> your "random" selection or NOT IN your "random" selection(s).
EDIT: Apparently I'm wrong about the need to specify any column name, you can do it using the assigned ROW_NUMBER.. But I'm not going to delete my answer because it references your use of EXCEPT which was discussed in the comments. You cannot do it without deriving some column name like that from ROW_NUMBER
You could do something like this (SQL 2008)
DECLARE #Original TABLE ([Id] INT)
INSERT INTO #Original(ID) VALUES(1)
INSERT INTO #Original(ID) VALUES(2)
INSERT INTO #Original(ID) VALUES(3)
SELECT * FROM #Original;
WITH CTE AS
(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS ROW, ID FROM #Original)
DELETE #Original
FROM #Original O
INNER JOIN CTE ON O.ID = CTE.ROW
WHERE ROW > 1
SELECT * FROM #Original
It seems like the simplest answer may be the best. The following should work:
Declare #count int
Set #count=(Select count(*) from DeleteExceptTop1)-1
Delete top (#count) from DeleteExceptTop1
I know it has been answered but what about?
DELETE
FROM [dbo].[DeleteExceptTop1]
Where Id not in (
SELECT TOP 1 * FROM [dbo].[DeleteExceptTop1])

Test ALL rows exists

It is very simple to test when row exists or not.
if exists(select * from dbo.APQP_head where zestaw=#zestaw)
I want to test in my query whether all rows satisfy the condition.
I need use some query like this
if All exists(select * from dbo.APQP_head where zestaw=#zestaw and type=3)
But this syntax is not correct.
if NOT exists(select * from dbo.APQP_head where zestaw<>#zestaw OR type<>3)
--all rows satisfy the condition
if your columns can be nullable, then
if NOT exists(select * from dbo.APQP_head where zestaw<>#zestaw OR type<>3)
AND NOT exists(select * from dbo.APQP_head where zestaw IS NULL OR type IS NULL)
This may perform better than an OR because it keep the AND and uses semi-joins
IF NOT EXISTS (
SELECT zestaw, [type] FROM #foo
EXCEPT
SELECT zestaw, [type] FROM #foo where zestaw=#zestaw and type=3
)
-- all rows etc
Edit, quick and dirty test (SQL Server 2008 R2 Express on workstation), the EXCEPT uses more IO (2 touches) but less CPU (more efficient plan)
If you replace #zestaw with a constant, the NOT EXISTS .. OR .. wins
CREATE TABLE excepttest (zestaw int, [type] int);
INSERT excepttest VALUES (1, 3);
GO
INSERT excepttest SELECT excepttest.* FROM excepttest
GO 21
SELECT COUNT(*) FROM excepttest
GO
CREATE INDEX IX_Test ON excepttest (zestaw, [type]);
GO
DECLARE #zestaw int = 1;
SET STATISTICS IO ON
SET STATISTICS TIME ON
if NOT exists(select * from excepttest where zestaw<>#zestaw OR [type]<>3)
SELECT 'all match'
ELSE
SELECT 'some match';
IF NOT EXISTS (
SELECT zestaw, [type] FROm excepttest
EXCEPT
SELECT zestaw, [type] FROm excepttest where zestaw=#zestaw and [type]=3
)
SELECT 'all match'
ELSE
SELECT 'some match';
SET STATISTICS IO OFF
SET STATISTICS TIME OFF
DROP TABLE excepttest
You can invert the conditions in the WHERE clause and the whole exists expression:
if NOT exists select(select * from dbo.APQP_head where zestaw <> #zestaw OR type <> 3)
This uses a well known fact that NOT(A1 AND A2 AND ... An) == NOT(A1) OR NOT(A2)... OR NOT(An).
if not exists(select * from dbo.APQP_head where not (zestaw=#zestaw and type=3))
this is a solution for some problems:
select distinct AccAccountId from AccAccountTafsilType where AccAccountId
in (select Id From AccountForSooratVaziatFunc(5))
and AccAccountId in (select AccAccountId from AccAccountTafsilType where
TafsilTypeId in (select Id from AccTafsilType where ComplexId = 5 and
Code = 115))
and AccAccountId in (select AccAccountId from AccAccountTafsilType where
TafsilTypeId in (select Id from AccTafsilType where ComplexId = 5 and
Code = 116))