searching across column value pairs -- SQL server - sql

i'm searching the database for matching values of a column pair.
say, the table T1 has these 3 columns:
(id, this, that)
and the query is
select id from T1 where
this = 'aValue' and that = 'bbb'
OR this = 'CCCC' and that = 'DDD'
OR this = 'EE' and that = 'EEE'.
is there a shortcut to this?
if i were searching on the values of one column only, say "this", it was easy:
select id from T1 where this in ('aa', 'bbbb', 'cccc')
i'm using SQL Server.
TIA.

You can use the VALUES clause to construct a table value, then inner join against this to get all matches:
SELECT id
FROM T1
INNER JOIN (
VALUES ('aValue', 'bbb'), ('CCCC', 'DDD'), ('EE', 'EEE')
) AS C(x,y)
ON this = C.x AND that = C.y

Related

How to combine two rows under specific condition

I have following table:
CREATE table table1 (id int , cd date, ct TIME, co text)
INSERT INTO table1
VALUES (0, '1/1/2018', '12:00:00', 'B'),
(1, '1/1/2018', '12:30:00', 'BC'),
(2, '1/12/2018', '12:00:00', 'B'),
(3, '1/22/2018', '12:00:00', 'BC')
I need to combine "co" column when values of "cd" and "ct" columns are the same for 'B' AND 'BC' or only values of "ct" are different and display values of record 'B' for 'B-BC'.
for the above table1 records I need following result:
"id" "cd" "ct" "co"
"0" "1/1/2018" "12:00:00 PM" "B-BC"
"2" "1/12/2018" "12:00:00 PM" "B"
"3" "1/22/2018" "12:00:00 PM" "BC"
What is the most effective way to do that in postgresql 8.4?
I created two common table expressions (CTE) to come up with a list of B and BC records that will be combined. The logic is get B record and BC record. Then do a left join from table1 to cte_A table and left join to cte_B table but cte_b.id_del is null. This will remove the id found in cte_B. Lastly, do a case when to use a new co value (B-BC) for id found in cte_A table. See demo here: http://sqlfiddle.com/#!15/62caa/49
with cte_a as (select a.id as id_keep
from table1 a
inner join table1 b on a.cd=b.cd
and a.co='B' and b.co='BC')
,cte_b as (select b.id as id_del
from table1 a
inner join table1 b on a.cd=b.cd
and a.co='B' and b.co='BC')
select t1.id,
t1.cd,
t1.ct,
case when cte_a.id_keep is null
then t1.co
else 'B-BC' end as co
from table1 t1
left join cte_a
on t1.id=cte_a.id_keep
left join cte_b
on t1.id = cte_b.id_del
where cte_b.id_del is null;
You could use string_agg:
SELECT cd, MIN(id) AS id, min(ct) AS ct, string_agg(co, '-' ORDER BY ct) AS co
FROM table1
GROUP BY cd;
DBFiddle Demo
EDIT:
Lovely answer! but ct for 'B' is not always minimum value. I need the ct, which is specifically in record 'B', which may be greater 'ct' for 'BC' or less
You could use windowed function:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY cd ORDER BY ct ASC) AS rn FROM table1
)
SELECT id, cd, ct,s.co
FROM cte
JOIN LATERAL(SELECT string_agg(co, '-' ORDER BY ct) AS co
FROM cte c2 WHERE c2.cd=cte.cd)s ON TRUE
WHERE rn=1;
DBFiddle Demo2

Oracle SQL - Comparing Rows

I have a problem I'm working on with Oracle SQL that goes something like this.
TABLE
PurchaseID CustID Location
----1------------1-----------A
----2------------1-----------A
----3------------2-----------A
----4------------2-----------B
----5------------2-----------A
----6------------3-----------B
----7------------3-----------B
I'm interested in querying the Table to return all instances where the same customer makes a purchase in different locations. So, for the table above, I would want:
OUTPUT
PurchaseID CustID Location
----3------------2-----------A
----4------------2-----------B
----5------------2-----------A
Any ideas on how to accomplish this? I haven't been able to think of how to do it, and most of my ideas seem like they would be pretty clunky. The database I'm using has 1MM+ records, so I don't want it to run too slowly.
Any help would be appreciated. Thanks!
SELECT *
FROM YourTable T
WHERE CustId IN (SELECT CustId
FROM YourTable
GROUP BY CustId
HAVING MIN(Location) <> MAX(Location))
You should be able to use something similar to the following:
select purchaseid, custid, location
from yourtable
where custid in (select custid
from yourtable
group by custid
having count(distinct location) >1);
See SQL Fiddle with Demo.
The subquery in the WHERE clause is returning all custids that have a total number of distinct locations that are greater than 1.
In English:
Select a row if another row exists with the same customer and a different location.
In SQL:
SELECT *
FROM atable t
WHERE EXISTS (
SELECT *
FROM atable
WHERE CustID = t.CustID
AND Location <> t.Location
);
Here's one approach using a sub-query
SELECT T1.PurchaseID
,T1.CustID
,T1.Location
FROM YourTable T1
INNER JOIN
(SELECT T2.CustID
,COUNT (DISTINCT T2.Location )
FROM YourTable T1
GROUP BY
T2.CustID
HAVING COUNT (DISTINCT T2.Location )>1
) SQ
ON SQ.CustID = T1.CustID
This should only require one full table scan.
create table test (PurchaseID number, CustID number, Location varchar2(1));
insert into test values (1,1,'A');
insert into test values (2,1,'A');
insert into test values (3,2,'A');
insert into test values (4,2,'B');
insert into test values (5,2,'A');
insert into test values (6,3,'B');
insert into test values (7,3,'A');
with repeatCustDiffLocations as (
select PurchaseID, custid, location, dense_rank () over (partition by custid order by location) r
from test)
select b.*
from repeatCustDiffLocations a, repeatCustDiffLocations b
where a.r > 1
and a.custid = b.custid;
This makes most sense to me as I was trying to return the rows with the same values throughout the table, specifically for two columns as shown in this stackoverflow answer here.
The answer to your problem in this format is:
SELECT DISTINCT a.*
FROM TEST a
INNER JOIN TEST b
ON a.CUSTOMERID = b.CUSTOMERID AND
a.LOCATION <> b.LOCATION;
However, the solution to a problem such as mine with two columns having matching values in multiple rows (2 in this instance, would yield no results because all PurchaseID's are unique):
SELECT DISTINCT a.*
FROM TEST a
INNER JOIN TEST b
ON a.CUSTOMERID = b.CUSTOMERID AND
a.PURCHASEID = b.PURCHASEID AND
a.LOCATION <> b.LOCATION;
Although, this wouldn't return the correct results based on the what needs to be queried, it shows that the query logic works
SELECT DISTINCT a.*
FROM TEST a
INNER JOIN TEST b
ON a.CUSTOMERID = b.CUSTOMERID AND
a.PURCHASEID <> b.PURCHASEID AND
a.LOCATION = b.LOCATION;
If anyone wants to try in Oracle here is the table and values to insert:
CREATE TABLE TEST (
PurchaseID integer,
CustomerID integer,
Location varchar(1));
INSERT ALL
INTO TEST VALUES (1, 1, 'A')
INTO TEST VALUES (2, 1, 'A')
INTO TEST VALUES (3, 2, 'A')
INTO TEST VALUES (4, 2, 'B')
INTO TEST VALUES (5, 2, 'A')
INTO TEST VALUES (6, 3, 'B')
INTO TEST VALUES (7, 3, 'B')
SELECT * FROM DUAL;

Determine which values are not in sql table

I have this query in oracle:
select * from table where col2 in (1,2,3,4);
lets say I got this result
col1 | col2
-----------
a 1
b 2
My 'in (1,2,3,4)' part has like 20 or more options, how can I determinate which values I don't found in my table? in my example 3 and 4 doesn't exist in the table
You can't in the way you want.
You need to insert the values you want to find into a table and than select all the values which don't exist in the desired table.
Lets say the data you want to find is in A and you want to know which doesn't exist in B.
SELECT *
FROM table_a A
WHERE NOT EXISTS (SELECT *
FROM table_b B
WHERE B.col1 = A.col1);
IN lists are stupid, or at least not very useful. Use a SQL Type collection to store your values instead because we can turn them into tables.
In this example I'm using the obscure SYS.KU$_OBJNUMSET type, which is the only nested table of Number I know of on 10g. (There's lots more in 11g).
So
select t.column_value
from table ( SYS.KU$_OBJNUMSET (1,2,3,4) ) t
left join your_table
on col2 = t.column_value
where col2 is null;
Here would be a way to do it if you're just using integers for your specific example:
SELECT *
FROM (
Select Rownum r
From dual
Connect By Rownum IN (1,2,3,4)
) T
LEFT JOIN YourTable T2 ON T.r = T2.Col2
WHERE T2.Col2 IS NULL
And the Fiddle.
This creates a table out of your where criteria 1,2,3,4 and uses that to LEFT JOIN on.
--EDIT
Because values aren't ints, here is another "ugly" option:
SELECT *
FROM (
Select 'a' r From dual UNION
Select 'b' r From dual UNION
Select 'c' r From dual UNION
Select 'd' r From dual
) T
LEFT JOIN YourTable T2 ON T.r = T2.Col2
WHERE T2.Col2 IS NULL
http://www.sqlfiddle.com/#!4/5e769/2
Good luck.

Join or Union or Subquery or something else?

I have two tables which I need to join/union for a distinct set - but every query I've tried so far results in too few/too many rows returned.
table one table two
ID Type
Type SubType
SubType
Percentage
Category
Table one is the main data set, table two is lookup values.
The data is something like this:
table one
1, A, ZZZ, 20.00, CAR
2, A, YYY, 80.00, CAR
3, B, YYY, 100.00, VAN
4, A, WWW, 100.00, BUS
table two
A, WWW
A, XXX
A, YYY
A, ZZZ
B, YYY
B, ZZZ
What I'm looking to end up with is the following data set
1, A, YYY, 80.00, CAR
2, A, ZZZ, 20.00, CAR
0, A, WWW, 0.00, CAR
0, A, XXX, 0.00, CAR
3, B, YYY, 100.00, VAN
0, B, ZZZ, 0.00, VAN
4, A, WWW, 100.00, BUS
0, A, XXX, 0.00, BUS
0, A, YYY, 0.00, BUS
0, A, ZZZ, 0.00, BUS
So, in other words I want all the data from table one PLUS any missing SubTypes from table two that match the Type of table one which would also include zero values for ID and Percentage, but keep the Category from the data set selected.
This works fine with an outer join if I only have one row in table one, but for each subsequent row my query adds more rows from table two. I think I need some sort of union or subquery that is aware of other rows selected from table one so will only select non-matching rows from table two.
Any help most appreciated!
Edit:
This is my current query (the more I think about this the more wrong it looks)
SELECT CASE WHEN t1.SubType = t2.SubType THEN t1.ID ELSE 0 END,
t1.Type, t2.SubType, CASE WHEN t1.SubType = t2.SubType THEN
t1.Percentage ELSE 0 END, t1.Category
FROM one t1
LEFT OUTER JOIN two t2
ON t2.Type = t1.Type
This gives me 8 rows - duplicated for each row selected from table one.
Another Edit:
Adding a couple more rows to table one I think explains this problem better. So the SQL to create tables and add rows looks like this:
create table one (ID int,Type nvarchar(1),SubType nvarchar(3),Percentage decimal(5,2),Category nvarchar(3))
create table two (Type nvarchar(1),SubType nvarchar(3))
insert into one values (1, 'A', 'ZZZ', 20.00, 'CAR')
insert into one values (2, 'A', 'YYY', 80.00, 'CAR')
insert into one values (3, 'B', 'YYY', 100.00, 'VAN')
insert into one values (4, 'A', 'WWW', 100.00, 'BUS')
insert into two values ('A', 'WWW')
insert into two values ('A', 'XXX')
insert into two values ('A', 'YYY')
insert into two values ('A', 'ZZZ')
insert into two values ('B', 'YYY')
insert into two values ('B', 'ZZZ')
I think this will do what you want:
SELECT ID, Type, SubType, Percentage, Category
FROM Table1
UNION ALL
SELECT 0, Type, SubType, 0.00, 'CAR'
FROM Table2 t2
LEFT JOIN t1 ON t1.Type = t2.Type AND t1.SubType = t2.SubType
WHERE t1.ID IS NULL
SELECT
ISNULL(t1.ID),
t2.Type,
t2.SubType,
ISNULL(t1.Percentage, 0),
ISNULL(t1.Category, 'CAR')
FROM Table2 t2
INNER JOIN (SELECT DISTINCT Type FROM Table1) f ON t2.Type = f.Type
LEFT JOIN Table1 t1 ON t2.Type = t1.Type AND t2.SubType = t1.SubType
You can do this without a UNION, just a LEFT JOIN:
UPDATED FOLLOWING COMMENT
SELECT ISNULL(t1.ID,0) Id, t2.Type, t2.SubType, ISNULL(t1.percentage,0) Percentage, ISNULL(t1.Category,'CAR') Category
FROM Table2 t2
LEFT JOIN Table1 t1
ON t1.Type = t2.Type AND t1.SubType = t2.SubType
WHERE t2.Type IN (SELECT DISTINCT Type FROM Table1)
Ok, this will give you the result you want, but its assigning the category as 'CAR', if you don't want that, then you should tell us what it should be.
Thanks for everyone's help on this, but I've worked it out myself finally...
SELECT ID, Type, SubType, Percentage, Category
FROM one
UNION
SELECT DISTINCT 0, t2.Type, t2.SubType, 0.00, t1.Category
FROM one t1
INNER JOIN two t2 ON t2.Type = t1.Type AND t2.SubType NOT IN
(SELECT t3.SubType FROM one t3 WHERE t3.Category = t1.Category and t3.Type = t1.Type)
ORDER BY Category, Type, SubType

Generic SQL questions

I have a table A
ID Term
10 A
10 B
10 C
20 A
20 B
20 E
what's the best way to write a sql to
get ID 10, if I try to find (A,B,C)
get NOTHING if I try to find (A,B)
get ID 20, if I try to find NOT in (C,D)
.
Select distinct ID from TableA where Term in (A,B,C) will return both 10 and 20
Select distinct ID from TableA where Term in (A,B) will also return both 10 and 20
Select distinct ID from TableA where Term NOT in (C,D) will also return both 10 and 20
Thanks!
1.
SELECT ID
FROM TableA
WHERE Term IN ('A','B','C')
GROUP BY ID
HAVING COUNT(ID)=3
LIMIT 1
Here 3 would be the length of the set (A,B,C in this case). 2 & 3 could probably be some variation of the above.
I assume you want one form of query that can be used to answer all three questions, not a different kind of query for each question.
These solutions take advantage of COUNT() not counting NULLs. When the OUTER JOIN does not match a row in t2, it results in NULL for all columns from t2.
get ID 10, if I try to find (A,B,C)
ID 10 has three distinct term values, and we're searching for all three.
SELECT t1.ID
FROM TableA t1 LEFT OUTER JOIN TableA t2
ON (t1.ID = t2.ID AND t1.term = t2.term AND t2.term IN ('A', 'B', 'C'))
GROUP BY t1.ID
HAVING COUNT(t1.term) = COUNT(t2.term);
get NOTHING if I try to find (A,B)
Both ID 10 and ID 20 have three distinct term values, but our search is only for two. The counts are 3 = 2 for both IDs, so neither have equal counts.
SELECT t1.ID
FROM TableA t1 LEFT OUTER JOIN TableA t2
ON (t1.ID = t2.ID AND t1.term = t2.term AND t2.term IN ('A', 'B'))
GROUP BY t1.ID
HAVING COUNT(t1.term) = COUNT(t2.term);
get ID 20, if I try to find NOT in (C,D)
ID 20 has three distinct term values, and all three of them are NOT 'C' or 'D'. So the counts are equal.
SELECT t1.ID
FROM TableA t1 LEFT OUTER JOIN TableA t2
ON (t1.ID = t2.ID AND t1.term = t2.term AND t2.term NOT IN ('C', 'D'))
GROUP BY t1.ID
HAVING COUNT(t1.term) = COUNT(t2.term);
Q1
SQLite version 3.6.10
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> create table A(ID smallint, term varchar(1));
sqlite> insert into A values(10, 'A');
sqlite> insert into A values(10, 'B');
sqlite> insert into A values(10, 'C');
sqlite> insert into A values(20, 'A');
sqlite> insert into A values(20, 'B');
sqlite> insert into A values(20, 'E');
sqlite> SELECT ID FROM A WHERE TERM = 'A' INTERSECT SELECT ID FROM A WHERE TERM
= 'B' INTERSECT SELECT ID FROM A WHERE TERM = 'C';
10
Q2
sqlite> SELECT ID FROM A WHERE TERM = 'A' EXCEPT SELECT ID FROM A WHERE TERM = '
B';
returns no results. EXCEPT can also be called MINUS in some versions of SQL
Q3
sqlite> SELECT ID FROM A EXCEPT SELECT ID FROM A WHERE TERM = 'C' UNION SELECT I
D FROM A WHERE TERM = 'D';
20
CREATE TABLE mySearch (
search_id INT,
val CHAR(1),
is_required INT,
is_excluded INT
)
INSERT INTO mySearch VALUES (1, 'A', 1, 0) -- Search1 : A is required
INSERT INTO mySearch VALUES (1, 'B', 1, 0) -- Search1 : B is required
INSERT INTO mySearch VALUES (1, 'C', 1, 0) -- Search1 : C is required
INSERT INTO mySearch VALUES (2, 'A', 1, 0) -- Search2 : A is required
INSERT INTO mySearch VALUES (2, 'B', 1, 0) -- Search2 : B is required
INSERT INTO mySearch VALUES (3, 'C', 0, 1) -- Search3 : C is excluded
INSERT INTO mySearch VALUES (3, 'D', 0, 1) -- Search3 : D is excluded
SELECT
[search].search_id,
[data].id
FROM
TableA AS [data]
LEFT JOIN
my_search AS [search]
ON [search].val = [data].Term
GROUP BY
[search].search_id,
[data].ID
HAVING
ISNULL(SUM([search].is_included),0) = (SELECT SUM(is_included) FROM mySearch WHERE search_id = [search].search_id)
AND MAX([search].is_excluded) IS NULL
Should satisfy all three seaches in one query. Unfortunately I can't test it as I'm at the in-laws house and they don't have geek toys to test on ;)