Static in clause sql - sql

I'm trying to put more than one static in clause in the SQL (oracle) and its not working, any one has an idea or a work around it. Below is what I'm trying to do
select *
from Table
where ('1', '2') in ('1', '2', '3')
I know it can be done using OR clause but I don't want to use it as there are too many arguments.

I'm guess your query is intended to flexible handle a bunch of input options and dynamically adjust the output in some way. Perhaps this is an approach that will work. Usually these BI systems are going to still want a syntactically valid query so unfortunately you can't build the whole query dynamically.
with list(val) as (
select val from master_list_of_values where val in (? /* BI_Parameter */)
)
select * from Table
where
A in (select id from list)
or B in (select id from list)
UPDATE: Based on your edit, this will work although I don't know if it will always generate great plans:
with security_test(passed) as (
select count(*) as passed
from security_groups
where group_id in (? /* BI_Parameter */) and group_id in (/* hard-coded list */)
)
select * from Table
where (select passed from security_test > 0)
You could use also hard-code the list in a values expression instead of using a "security_groups" table.
with security_test(passed) as (
select count(*) as passed
from (values (1), (2)) security_groups(group_id)
where group_id in (? /* BI_Parameter */)
)
select * from Table
where (select passed from security_test > 0)
Here's another thought...
with security_test(passed) as (
select count(*) as passed
from (values (1), (2)) security_groups(group_id)
where group_id in (? /* BI_Parameter */)
having count(*) > 0 /* will this collapse to zero rows? */
)
select * from Table, security_test

It can't be done how you want it done, because of ambiguity. For instance, would that syntax mean "1 and 2 are in 1,2,3" or would it mean "1 is in 1,2,3 or 2 is in 1,2,3".
I think it would really help to see what is generating this static in clause and what the actual end goal is... Then I'll edit my answer to help more.
Edit
I think there is just a simple logical flaw in your application design. Would it not also work to remove the (1,2,3) clause altogether, and then rather than consider your '1,2' as static, just reference the column. For example, this achieves what you want with a little tweaking of your (incorrectly developed) BI report:
select *
from Table
where myCharColumn in ('1', '2')

Related

Using CONTAINS to find items IN a table

I'm trying to write a SP that will allow users to search on multiple name strings, but supports LIKE functionality. For example, the user's input might be a string 'Scorsese, Kaurismaki, Tarkovsky'. I use a split function to turn that string into a table var, with one column, as follows:
part
------
Scorsese
Kaurismaki
Tarkovsky
Then, normally I would return any values from my table matching any of these values in my table var, with an IN statement:
select * from myTable where lastName IN (select * from #myTableVar)
However, this only returns exact matches, and I need to return partial matches. I'm looking for something like this, but that would actually compile:
select * from myTable where CONTAINS(lastName, select * from #myTableVar)
I've found other questions where it's made clear that you can't combine LIKE and IN, and it's recommended to use CONTAINS. My specific question is, is it possible to combine CONTAINS with a table list of values, as above? If so, what would that syntax look like? If not, any other workarounds to achieve my goal?
I'm using SQL Server 2016, if it makes any difference.
You can use EXISTS
SELECT * FROM myTable M
WHERE
EXISTS( SELECT * FROM #myTableVar V WHERE M.lastName like '%'+ V.part +'%' )
Can your parser built the entire statement? Will that get you what you want?
select *
from myTable
where CONTAINS
(lastName,
'"Scorsese" OR "Kaurismaki" OR "Tarkovsky"'
)
This can be done using CHARINDEX function combined with EXISTS:
select *
from myTable mt
where exists(select 1 from #myTableVar
where charindex(mt.lastName, part) > 0
or charindex(part, mt.lastName) > 0)
You might want to omit one of the conditions in the inner query, but I think this is what you want.

'In' clause in SQL server with multiple columns

I have a component that retrieves data from database based on the keys provided.
However I want my java application to get all the data for all keys in a single database hit to fasten up things.
I can use 'in' clause when I have only one key.
While working on more than one key I can use below query in oracle
SELECT * FROM <table_name>
where (value_type,CODE1) IN (('I','COMM'),('I','CORE'));
which is similar to writing
SELECT * FROM <table_name>
where value_type = 1 and CODE1 = 'COMM'
and
SELECT * FROM <table_name>
where value_type = 1 and CODE1 = 'CORE'
together
However, this concept of using 'in' clause as above is giving below error in 'SQL server'
ERROR:An expression of non-boolean type specified in a context where a condition is expected, near ','.
Please let know if their is any way to achieve the same in SQL server.
This syntax doesn't exist in SQL Server. Use a combination of And and Or.
SELECT *
FROM <table_name>
WHERE
(value_type = 1 and CODE1 = 'COMM')
OR (value_type = 1 and CODE1 = 'CORE')
(In this case, you could make it shorter, because value_type is compared to the same value in both combinations. I just wanted to show the pattern that works like IN in oracle with multiple fields.)
When using IN with a subquery, you need to rephrase it like this:
Oracle:
SELECT *
FROM foo
WHERE
(value_type, CODE1) IN (
SELECT type, code
FROM bar
WHERE <some conditions>)
SQL Server:
SELECT *
FROM foo
WHERE
EXISTS (
SELECT *
FROM bar
WHERE <some conditions>
AND foo.type_code = bar.type
AND foo.CODE1 = bar.code)
There are other ways to do it, depending on the case, like inner joins and the like.
If you have under 1000 tuples you want to check against and you're using SQL Server 2008+, you can use a table values constructor, and perform a join against it. You can only specify up to 1000 rows in a table values constructor, hence the 1000 tuple limitation. Here's how it would look in your situation:
SELECT <table_name>.* FROM <table_name>
JOIN ( VALUES
('I', 'COMM'),
('I', 'CORE')
) AS MyTable(a, b) ON a = value_type AND b = CODE1;
This is only a good idea if your list of values is going to be unique, otherwise you'll get duplicate values. I'm not sure how the performance of this compares to using many ANDs and ORs, but the SQL query is at least much cleaner to look at, in my opinion.
You can also write this to use EXIST instead of JOIN. That may have different performance characteristics and it will avoid the problem of producing duplicate results if your values aren't unique. It may be worth trying both EXIST and JOIN on your use case to see what's a better fit. Here's how EXIST would look,
SELECT * FROM <table_name>
WHERE EXISTS (
SELECT 1
FROM (
VALUES
('I', 'COMM'),
('I', 'CORE')
) AS MyTable(a, b)
WHERE a = value_type AND b = CODE1
);
In conclusion, I think the best choice is to create a temporary table and query against that. But sometimes that's not possible, e.g. your user lacks the permission to create temporary tables, and then using a table values constructor may be your best choice. Use EXIST or JOIN, depending on which gives you better performance on your database.
Normally you can not do it, but can use the following technique.
SELECT * FROM <table_name>
where (value_type+'/'+CODE1) IN (('I'+'/'+'COMM'),('I'+'/'+'CORE'));
A better solution is to avoid hardcoding your values and put then in a temporary or persistent table:
CREATE TABLE #t (ValueType VARCHAR(16), Code VARCHAR(16))
INSERT INTO #t VALUES ('I','COMM'),('I','CORE')
SELECT DT. *
FROM <table_name> DT
JOIN #t T ON T.ValueType = DT.ValueType AND T.Code = DT.Code
Thus, you avoid storing data in your code (persistent table version) and allow to easily modify the filters (without changing the code).
I think you can try this, combine and and or at the same time.
SELECT
*
FROM
<table_name>
WHERE
value_type = 1
AND (CODE1 = 'COMM' OR CODE1 = 'CORE')
What you can do is 'join' the columns as a string, and pass your values also combined as strings.
where (cast(column1 as text) ||','|| cast(column2 as text)) in (?1)
The other way is to do multiple ands and ors.
I had a similar problem in MS SQL, but a little different. Maybe it will help somebody in futere, in my case i found this solution (not full code, just example):
SELECT Table1.Campaign
,Table1.Coupon
FROM [CRM].[dbo].[Coupons] AS Table1
INNER JOIN [CRM].[dbo].[Coupons] AS Table2 ON Table1.Campaign = Table2.Campaign AND Table1.Coupon = Table2.Coupon
WHERE Table1.Coupon IN ('0000000001', '0000000002') AND Table2.Campaign IN ('XXX000000001', 'XYX000000001')
Of cource on Coupon and Campaign in table i have index for fast search.
Compute it in MS Sql
SELECT * FROM <table_name>
where value_type + '|' + CODE1 IN ('I|COMM', 'I|CORE');

SQL IN() operator with condition inside

I've got table with few numbers inside (or even empty): #states table (value int)
And I need to make SELECT from another table with WHERE clause by definite column.
This column's values must match one of #states numbers or if #states is empty then accept all values (like there is no WHERE condition for this column).
So I tried something like this:
select *
from dbo.tbl_docs docs
where
docs.doc_state in(iif(exists(select 1 from #states), (select value from #states), docs.doc_state))
Unfortunately iif() can't return subquery resulting dataset. I tried different variations with iif() and CASE but it wasn't successful. How to make this condition?
select *
from dbo.tbl_docs docs
where
(
(select count(*) from #states) > 0
AND
docs.doc_state in(select value from #states)
)
OR
(
(select count(*) from #states)=0
AND 1=1
)
Wouldn't a left join do?
declare #statesCount int;
select #statesCount = count(1) from #states;
select
docs.*
from dbo.tbl_docs docs
left join #states s on docs.doc_state = s.value
where s.value is not null or #statesCount = 0;
In general, whenever your query contains sub-queries, you should stop for five minutes, and think hard about whether you really need a sub-query at all.
And if you've got a server capable of doing that, in many cases it might be better to preprocess the input parameters first, or perhaps use constructs such as MS SQL's with.
select *
from dbo.tbl_docs docs
where exists (select 1 from #states where value = doc_state)
or not exists (select 1 from #state)

sql insert using select, case and subquery inside

select * into #transacTbl from tmpTrans
insert
select
(case when tmpT.TranStatus = 10
then(
select ID, 'Returned')
else(
select ID, 'GoodSale')
end)
from
(
select * from MainTransacSource
) as tmpT
I want to be able to insert the details of a transaction into a different table with a label if it is a returned or good sale/transaction. I did this to avoid the cursor so please avoid giving a solution using a cursor.
I know the code looks good but what I'm experiencing is that, the case statement only returns one value via subquery.
This is a simplified version of the code; I have at least 6 types of cases and should be able to insert by ROW. I hate to think that I have to repeat each case per column because the actual number of columns is about 38.
You may suggest another work-around if this doesn't fit the logic. Of course, without a cursor.
Without access to your tables and not knowing more about what precisely you want to acheive, try something like this:
select * into #transacTbl from tmpTrans
insert
select tmpT.ID,
(case when tmpT.TranStatus = 10
then 'Returned'
else 'GoodSale'
end)
from
(select * from MainTransacSource) as tmpT <OR simply MainTransacSource tmpT (maybe)>
Cheers.

TSQL NOT EXISTS Why is this query so slow?

Debugging an app which queries SQL Server 05, can't change the query but need to optimise things.
Running all the selects seperately are quick <1sec, eg: select * from acscard, select id from employee... When joined together it takes 50 seconds.
Is it better to set uninteresting accesscardid fields to null or to '' when using EXISTS?
SELECT * FROM ACSCard
WHERE NOT EXISTS
( SELECT Id FROM Employee
WHERE Employee.AccessCardId = ACSCard.acs_card_number )
AND NOT EXISTS
( SELECT Id FROM Visit
WHERE Visit.AccessCardId = ACSCard.acs_card_number )
ORDER by acs_card_id
Do you have indexes on Employee.AccessCardId, Visit.AccessCardId, and ACSCard.acs_card_number?
The SELECT clause is not evaluated in an EXISTS clause. This:
WHERE EXISTS(SELECT 1/0
FROM EMPLOYEE)
...should raise an error for dividing by zero, but it won't. But you need to put something in the SELECT clause for it to be a valid query - it doesn't matter if it's NULL or a zero length string.
In SQL Server, NOT EXISTS (and NOT IN) are better than the LEFT JOIN/IS NULL approach if the columns being compared are not nullable (the values on either side can not be NULL). The columns compared should be indexed, if they aren't already.