I'm using SSMS 17.9.1 - I have a table with a ContractNo and RightCodes columns. See image attachment for sample data:
I need a SELECT statement that will return rows where RightsCodes LIKE '904' or '908', but where there are no other rows with the same ContractNo that have RightCodes LIKE '922' or '923' or '924'.
So in the example data I would expect Rows 1 and 8 to be returned.
There can be x number of rows that have the same ContractNo. And RightCodes can have 1 to x number of values per ContractNo
Thank you very much in advance.
Make some data (noting the terrible way to store data):
DECLARE #bad_table TABLE (
[row] INT,
contractno INT,
rightcodes VARCHAR(512));
INSERT INTO #bad_table SELECT 1, 28205, '904,908';
INSERT INTO #bad_table SELECT 2, 28205, '911,913';
INSERT INTO #bad_table SELECT 3, 28194, '904';
INSERT INTO #bad_table SELECT 4, 28194, '923,924';
INSERT INTO #bad_table SELECT 5, 28194, '922';
INSERT INTO #bad_table SELECT 6, 28192, '908,923';
INSERT INTO #bad_table SELECT 7, 28192, '911';
INSERT INTO #bad_table SELECT 8, 28193, '904';
Write a query to pull out the data as required:
SELECT
*
FROM
#bad_table b
WHERE
(b.rightcodes LIKE '%904%' OR b.rightcodes LIKE '%908%')
AND NOT EXISTS (SELECT * FROM #bad_table xb WHERE xb.contractno = b.contractno AND (xb.rightcodes LIKE '%922%' OR xb.rightcodes LIKE '%923%'));
This gives the results you want, rows 1 and 8.
I have a query which uses a IN Filter and works fine. I am wondering if there
is something like a wildcard char which will not filter anything
Select *
FROM [tbl_Leads]
where p_contact_first_name in ('Tom')
the above works as desired but what happens if i don't want to filter by anything and return all. I know i can create a second query and removing the IN clause but from the logic if possible it would be nicer if i can check for existence of filter value and if none present replace it with wildcard char
The IN operator doesn't allow wildcards or partial values to match. In fact it's just a syntactic sugar of a chaining of OR logical operators.
This query:
SELECT 1 FROM SomeTable AS T
WHERE T.Column IN (1, 2, 3, 4)
Is exactly the same as:
SELECT 1 FROM SomeTable AS T
WHERE
T.Column = 1 OR
T.Column = 2 OR
T.Column = 3 OR
T.COlumn = 4
And this is why having a NULL value with a NOT IN list will make all the logic result be UNKNOWN (hence interpreted as false and never return any record):
SELECT 1 FROM SomeTable AS T
WHERE T.Column NOT IN (1, 2, NULL, 4)
Will be:
SELECT 1 FROM SomeTable AS T
WHERE
NOT(
T.Column = 1 OR
T.Column = 2 OR
T.Column = NULL OR -- Always resolve to UNKNOWN (handled as false for the whole condition)
T.COlumn = 4
)
You have a few options to conditionally apply a filter like IN:
Use OR against another condition:
DECLARE #ApplyInFilter BIT = 0
SELECT 1 FROM SomeTable AS T
WHERE
(#ApplyInFilter = 1 AND T.Column IN (1, 2, 3, 4)) OR
#ApplyInFilter = 0
Avoid the query altogether (have to repeat whole statement):
DECLARE #ApplyInFilter BIT = 0
IF #ApplyInFilter = 1
BEGIN
SELECT 1 FROM SomeTable AS T
WHERE
T.Column IN (1, 2, 3, 4)
END
ELSE
BEGIN
SELECT 1 FROM SomeTable AS T
END
Use Dynamic SQL to conditionally omit the filter:
DECLARE #ApplyInFilter BIT = 0
DECLARE #DynamicSQL VARCHAR(MAX) = '
SELECT 1 FROM SomeTable AS T '
IF #ApplyInFilter = 1
SET #DynamicSQL += ' WHERE T.Column IN (1, 2, 3, 4) '
EXEC (#DynamicSQL)
Unfortunately, the best approach if you plan to have multiple conditional filters is the Dynamic SQL one. It will be the hardest to code but best for performance (with some caveats). Please read George's Menoutis link to fully understand pros and cons of each approach.
You can make use of not exists to get the desired results. From my understanding if you have a name like Tom you want only that row and if it does not you want all other rows to be displayed.
select 1 as ID, 'Tom' as Name into #temp
union all
select 2 as ID, 'Ben' as Name union all
select 3 as ID, 'Kim' as Name
union all
select 4 as ID, 'Jim' as Name
This query will check if Tom exists then display only that row if not display all.
select * from #temp
where name = 'TOm' or not exists (select 1 from #temp where name = 'Tom')
Result from above query:
ID Name
1 Tom
Lets test it, by deleting the row where Tom record is.
Delete from #temp
where name = 'Tom'
If you run the same query you get the following result.
select * from #temp
where name = 'TOm' or not exists (select 1 from #temp where name = 'Tom')
ID Name
2 Ben
3 Kim
4 Jim
As said by Dale Burrell, the fast way to implement dynamic search conditions (exactly what your problem is) is to put code like:
....and field=values or #searchThisField=0
The other solution would be dynamic sql.
I consider Erland Sommarskog's article to be the epitome of analyzing this specific subject.
Make two requests. The performance of these two queries will be better than that of a single universal query. You can compare the execution plan for these queries.
The two tables I am using are
Contacts (Contact_id, Contact_name,...)
Contacts_group_options (Contact_id, Group_id, Status)
I want to get all the contacts that are not part of a specific group from the Contacts_group_options table .
The problem is, if first table has Contact_ids 1, 2, 3, 4 and second table has Contact_ids 2, 3 for a Group_id = 1, I want result as 1 and 4. But I am getting 1, 3, 4, 1, 2, 4.
First 1 is compared with 2. Both are not same. Output 1, then 2 with 2 same so don't output, 3 with 2 output 3, 4 with 2 output 4,1 with 3 output 1,2 with 3, output 2,3 with 3 dont output,4 with 3 output 4.
stored procedure is:
Select
m.Contact_id,
m.Contact_code,
m.Contact_name,
m.Phone_number,
m.Mail_id,
m.Designation,
m.Department,
m.User_id,
m.User_type,
m.Status
from
Contacts as m,
Contact_group_options as b
,#tblType_contacts2group as i
where
b.Group_id = i.Group_id
and m.Contact_id != b.Contact_id
I think you can do this with a simple NOT EXISTS:
SELECT c.*
FROM Contacts AS c
WHERE
NOT EXISTS(
SELECT 1
FROM Contact_group_options
WHERE
Group_id = 1
AND Contact_id = c.Contact_id
)
You can try this:
-- NOT EXISTS
SELECT C.*
FROM Contacts C
WHERE NOT EXISTS (SELECT 1 FROM Contacts_group_options WHERE Group_id = 1 AND C.Contact_id)
-- NOT IN
SELECT *
FROM Contacts
WHERE Contact_id NOT IN (SELECT Contact_id FROM Contacts_group_options WHERE Group_id = 1)
Your result set includes so many rows because you actually have three tables there: Contacts, Contact_group_options and #tblType_contacts2group.
Maybe you wanted to write something like this:
-- compacted for to make it more readable
Select
m.Contact_id, m.Contact_code, m.Contact_name, m.Phone_number,
m.Mail_id, m.Designation, m.Department, m.User_id, m.User_type, m.Status
from Contacts as m
where not exists (select 1 from #tblType_contacts2group as i where i.Group_id = m.Group_id)
NOTE: Since #tblType_contacts2group is not clearly defined in your example, above query might not get the correct results.
As already Felix mentioned, you should always use proper joins. E.g. the above query could be like this:
Select
m.Contact_id, m.Contact_code, m.Contact_name, m.Phone_number,
m.Mail_id, m.Designation, m.Department, m.User_id, m.User_type, m.Status
from Contacts as m
left join #tblType_contacts2group i on i.Group_id = m.Group_id
where i.Group_id IS NULL -- any non-null column from i can be used here
However, keep in mind that, usually, LEFT JOIN with IS NULL is slower that NOT EXISTS equivalent.
Lets say I have a field. Lets call it Barcode1. Right now all Barcodes1 are 22 characters with each character is an integer.
Suppose there is a second field Barcode2. Both of these are varchar(22)
My condition in plain english terms is:
Barcode1 is identical to barcode2 except in digits 7,8 where for barcode2, digits 7 and 8 are the same thing in barcode1 plus 20
so
001214**54**54545654521523
549462**74**48634842135782
I also would like the negation of the where clause where rows that do NOT match the condition are returned.
Thank you.
I think this is what you want:
Example Data:
DECLARE #table TABLE ( barcode VARCHAR(22) )
INSERT INTO #table
(
barcode
)
SELECT '0012145454545654521523'
UNION ALL
SELECT '0012142454545654521523'
UNION ALL
SELECT '5494627448634842135782'
UNION ALL
SELECT '5494625448634842135782'
First Condition - meets 7,8 + 20
SELECT a.barcode,
b.barcode,
SUBSTRING(a.barcode, 7, 2) a,
SUBSTRING(b.barcode, 7, 2) b
FROM #table a
INNER JOIN #table b
ON SUBSTRING(a.barcode, 7, 2) + 20 = SUBSTRING(b.barcode, 7, 2)
AND a.barcode != b.barcode
returns:
barcode barcode a b
0012145454545654521523 5494627448634842135782 54 74
5494625448634842135782 5494627448634842135782 54 74
Negation where 7,8 + 20 doesn't exist
SELECT *
FROM #table a
WHERE NOT EXISTS ( SELECT TOP 1 1
FROM #table b
WHERE SUBSTRING(a.barcode, 7, 2) + 20 = SUBSTRING(b.barcode, 7, 2) )
returns:
0012142454545654521523
5494627448634842135782
You'll have to break that barcode up using string operations, something like:
WHERE
substring(barcode1, 0, 6) = substring(barcode2, 0, 6) AND
substring(barcode1, 9, 2) = substring(barcode2, 0, 9) AND
etc...
And since you'll be doing these comparisons on function results, indexes aren't going to be used. If this is a frequent operation, you'd be better off splitting up the barcode strings into individual fields so you can compare the individual chunks as fully separate indexable fields.
I have very simple problem that I can't solve. I need to do something like this:
select distinct * from (1, 1, 1, 2, 5, 1, 6).
Anybody can help??
Edit
The data comes as a text file from one of our clients. It's totally unformatted (it's a single, very long line of text), but it may be possible to do so in Excel. But it's not practical for me, because I will need to use these values in my sql query. It's not convenient to do so every time I need to run a query.
Available only on SQL Server 2008 and over is row-constructor in this form:
You could use
SELECT DISTINCT *
FROM (
VALUES (1), (1), (1), (2), (5), (1), (6)
) AS X(a)
For more information see:
MS official
http://www.sql-server-helper.com/sql-server-2008/row-value-constructor-as-derived-table.aspx
In general :
SELECT
DISTINCT
FieldName1, FieldName2, ..., FieldNameN
FROM
(
Values
( ValueForField1, ValueForField2,..., ValueForFieldN ),
( ValueForField1, ValueForField2,..., ValueForFieldN ),
( ValueForField1, ValueForField2,..., ValueForFieldN ),
( ValueForField1, ValueForField2,..., ValueForFieldN ),
( ValueForField1, ValueForField2,..., ValueForFieldN )
) AS TempTableName ( FieldName1, FieldName2, ..., FieldNameN )
In your case :
Select
distinct
TempTableName.Field1
From
(
VALUES
(1),
(1),
(1),
(2),
(5),
(1),
(6)
) AS TempTableName (Field1)
Simplest way to get the distinct values of a long list of comma delimited text would be to use a find an replace with UNION to get the distinct values.
SELECT 1
UNION SELECT 1
UNION SELECT 1
UNION SELECT 2
UNION SELECT 5
UNION SELECT 1
UNION SELECT 6
Applied to your long line of comma delimited text
Find and replace every comma with UNION SELECT
Add a SELECT in front of the statement
You now should have a working query
Have you tried using the following syntax?
select * from (values (1), (2), (3), (4), (5)) numbers(number)
If you want to select only certain values from a single table you can try this
select distinct(*) from table_name where table_field in (1,1,2,3,4,5)
eg:
select first_name,phone_number from telephone_list where district id in (1,2,5,7,8,9)
if you want to select from multiple tables then you must go for UNION.
If you just want to select the values 1, 1, 1, 2, 5, 1, 6 then you must do this
select 1
union select 1
union select 1
union select 2
union select 5
union select 1
union select 6
PostgreSQL gives you 2 ways of doing this:
SELECT DISTINCT * FROM (VALUES('a'),('b'),('a'),('v')) AS tbl(col1)
or
SELECT DISTINCT * FROM (select unnest(array['a','b', 'a','v'])) AS tbl(col1)
using array approach you can also do something like this:
SELECT DISTINCT * FROM (select unnest(string_to_array('a;b;c;d;e;f;a;b;d', ';'))) AS tbl(col1)
I know this is a pretty old thread, but I was searching for something similar and came up with this.
Given that you had a comma-separated string, you could use string_split
select distinct value from string_split('1, 1, 1, 2, 5, 1, 6',',')
This should return
1
2
5
6
String split takes two parameters, the string input, and the separator character.
you can add an optional where statement using value as the column name
select distinct value from string_split('1, 1, 1, 2, 5, 1, 6',',')
where value > 1
produces
2
5
6
This works on SQL Server 2005 and if there is maximal number:
SELECT *
FROM
(SELECT ROW_NUMBER() OVER(ORDER BY a.id) NUMBER
FROM syscomments a
CROSS JOIN syscomments b) c
WHERE c.NUMBER IN (1,4,6,7,9)
Using GROUP BY gives you better performance than DISTINCT:
SELECT *
FROM
(
VALUES
(1),
(1),
(1),
(2),
(5),
(1),
(6)
) AS A (nums)
GROUP BY A.nums;
If you need an array, separate the array columns with a comma:
SELECT * FROM (VALUES('WOMENS'),('MENS'),('CHILDRENS')) as X([Attribute])
,(VALUES(742),(318)) AS z([StoreID])
Another way that you can use is a query like this:
SELECT DISTINCT
LTRIM(m.n.value('.[1]','varchar(8000)')) as columnName
FROM
(SELECT CAST('<XMLRoot><RowData>' + REPLACE(t.val,',','</RowData><RowData>') + '</RowData></XMLRoot>' AS XML) AS x
FROM (SELECT '1, 1, 1, 2, 5, 1, 6') AS t(val)
) dt
CROSS APPLY
x.nodes('/XMLRoot/RowData') m(n);
If it is a list of parameters from existing SQL table, for example ID list from existing Table1, then you can try this:
select distinct ID
FROM Table1
where
ID in (1, 1, 1, 2, 5, 1, 6)
ORDER BY ID;
Or, if you need List of parameters as a SQL Table constant(variable), try this:
WITH Id_list AS (
select ID
FROM Table1
where
ID in (1, 1, 1, 2, 5, 1, 6)
)
SELECT distinct * FROM Id_list
ORDER BY ID;
I create a function on most SQL DB I work on to do just this.
CREATE OR ALTER FUNCTION [dbo].[UTIL_SplitList](#parList Varchar(MAX),#splitChar Varchar(1)=',')
Returns #t table (Column_Value varchar(MAX))
as
Begin
Declare #pos integer
set #pos = CharIndex(#splitChar, #parList)
while #pos > 0
Begin
Insert Into #t (Column_Value) VALUES (Left(#parList, #pos-1))
set #parList = Right(#parList, Len(#parList) - #pos)
set #pos = CharIndex(#splitChar, #parList)
End
Insert Into #t (Column_Value) VALUES (#parList)
Return
End
Once the function exists, it is as easy as
SELECT DISTINCT
*
FROM
[dbo].[UTIL_SplitList]('1,1,1,2,5,1,6',',')
Select user id from list of user id:
SELECT * FROM my_table WHERE user_id IN (1,3,5,7,9,4);
A technique that has worked for me is to query a table that you know has a large amount of records in it, including just the Row_Number field in your result
Select Top 10000 Row_Number() OVER (Order by fieldintable) As 'recnum' From largetable
will return a result set of 10000 records from 1 to 10000, use this within another query to give you the desired results
Use the SQL In function
Something like this:
SELECT * FROM mytable WHERE:
"VALUE" In (1,2,3,7,90,500)
Works a treat in ArcGIS