Select statement for Oracle SQL - sql

I have a table say,
column1 column2
a apple
a ball
a boy
b apple
b eagle
b orange
c bat
c ball
c cork
Now I would like to fetch column1 based on the rows that doesn't contain 'apple' and also ignore values in column1 if any of the rows have 'apple' in it. So in the table above only 'C' must be retured.
I am kind of new to Oracle SQL and I know Select column1 from table where column2 != 'apple' will not work. I need some help with this please.

You could use DISTINCT with NOT IN in following:
QUERY 1 using NOT IN
select distinct col1
from t
where col1 not in (select col1 from t where col2 = 'Apple')
QUERY 2 using NOT EXISTS
As per #jarlh comment you could use NOT EXISTS in following:
select distinct col1
from #t t1
where not exists (select 1 from #t t2 where col2 = 'Apple' and t1.col1 = t2.col1)
SAMPLE DATA
create table t
(
col1 nvarchar(60),
col2 nvarchar(60)
)
insert into t values
('a','apple')
,('a','ball')
,('a','boy')
,('b','apple')
,('b','eagle')
,('b','orange')
,('c','bat')
,('c','ball')
,('c','cork')

Assuming that column1 is NOT NULL you could use:
SELECT DISTINCT t.column1
FROM table_name t
WHERE t.column1 NOT IN (SELECT column1
FROM table_name
WHERE column2 = 'apple');
LiveDemo
To get all columns and rows change DISTINCT t.column1 to *.

Select * from tbl
Left join (
Select column1 from tbl
Where column2 like '%apple%'
Group by column1
) g on tbl.colum1 = g.column1
Where g.column1 is null
Seems to me that you need to find a summary of all colum1 values that have any reference to apple. Then list the rows that have no match to the summary list (g)

If I understand well, you need the values af column1 such that in your table does not exist a row with the same value of column1 and 'apple' in column2; you can translate this in SQL with:
Select column1
from your_table t
where not exists (
select 1
from your_table t2
where t2.column1 = t1.column1
and t2.column2= 'apple'
)
This is only one of the possible ways to get your result, soyou can rewrite it in many ways; I believe this way of writing is similar enough to the logics to clearly explain how a logic could be written in plain SQL.

Related

Redshift join wildcard

I am trying to do a wildcard search based on the result set of a subquery in Redshift. For example, Table A has first names and Table B has names which could be Last Name, First Name or First Name, Last Name. I want to return rows from Table B based on matches to a subset of Table A. I found the Similar To operator, but that only seems to work when I can hard-code the terms I am searching for. Is there a way I can achieve something like
SELECT col1 FROM Table_A WHERE col1 SIMILAR TO '%(SELECT distinct col2 FROM Table_B)%'
in order to achieve
SELECT col1 FROM Table_A WHERE col1 LIKE '%something%' OR col1 LIKE '%something else%'
This is what I ended up implementing after a recommendation from #GMB
CREATE TABLE test2 AS (
SELECT 'a' as val
UNION ALL
SELECT 'b' as val
UNION ALL
SELECT 'c' as val
);
CREATE TABLE test3 as (
SELECT 'apple' as name
UNION ALL
SELECT 'pear' as name
UNION ALL
SELECT 'plum' as name
);
SELECT * FROM test3
WHERE EXISTS (
SELECT 1
FROM test2 WHERE test3.name LIKE ('%'||val||'%')
)
You could use exists:
select col1
from table_a a
where exists (
select 1
from table_b b
where a.col1 similar to concat('%', b.col2, '%')
)

Left join update statement

I couldn't find any easy of doing table update with value from left join.
I want to do something like this:
UPDATE tbl1 p
LEFT JOIN (
select column1 , column2 from tbl2
union
select column1,column2 from tbl3
) c on c.column2=p.column2
SET p.column1 = nvl(c.column1, 'dummy');
UPDATE p SET p.column1 = nvl(c.column1, 'dummy')
from tbl1 p
LEFT JOIN (
select column1 , column2 from tbl2
union
select column1,column2 from tbl3
) c on c.column2=p.column2
I would recommend expressing this as:
UPDATE tbl1 p
SET p.column1 = COALESCE((SELECT column1 from tbl2 t2 WHERE t2.column2 = p.column2),
(SELECT column1 from tbl3 t3 WHERE t3.column2 = p.column2),
'dummy'
);
Each subquery can make use of indexes, so this should have better performance as well.
Try and see whether below code gives performance with CTE
Update tbl1 p
set column1 = nvl((with CTE as
(select column1 , column2 from tbl2
union
select column1,column2 from tbl3) select column1 from CTE where CTE.column2= p.column2 ),'dummy');
Use merge:
merge into tbl1 tgt
using (select min(column1) column1, column2
from (select column1, column2 from tbl2 union all
select column1, column2 from tbl3)
group by column2) src
on (tgt.column2 = src.column2)
when matched then update set tgt.column1 = src.column1
Let's say you have these tables:
create table tbl1(column1, column2) as (
select 0, 'A' from dual union all
select 0, 'B' from dual union all
select 0, 'C' from dual union all
select 0, 'D' from dual );
create table tbl2(column1, column2) as (
select 1, 'A' from dual union all
select 41, 'D' from dual );
create table tbl3(column1, column2) as (
select 2, 'B' from dual union all
select 42, 'D' from dual );
We have value for key A in tbl2, value for B in tbl3, value for C is absent in both source tables, and we have two problematic values for key D. You have to decide what to do in last case, use any aggregation function like min(), avg() or listagg() for strings. If such situation is not possible then you can simplify my statement, replace source subquery with simple union.
You could also use update, but in this case you have to add where clause and check existence of keys to avoid nullifying values and this makes code longer.
Result of merge:
COLUMN1 COLUMN2
---------- -------
1 A
2 B
0 C
41 D
Merge statement could have been the perfect solution if it was possible to update the same field in WHEN MATCHED and in WHEN NOT MATCHED. Anyway i fixed it like that:
The table is getting filled from external table using procedure.
Setting the field to 'dummy' for all records in the procedure inserting statement and use only MERGE WHEN MATCHED after works as i expected
PS: i couldnt make it work as #Ondřej Crha said.

How to combine multiple columns into one column?

I'm writing a query and want the results in one column
My current results return like this
Column1 Column2 column3
1 A CAT
I want the results to return like this
Column1
1
A
CAT
SELECT Column1 FROM TableName
UNION ALL
SELECT Column2 FROM TableName
UNION ALL
SELECT Column3 FROM TableName
If you don't want duplicate values, use UNION instead of UNION ALL.
You can also do this using UNPIVOT operator
SELECT Column123
FROM
(
SELECT Column1, Column2, Column3
FROM TableName
) AS tmp
UNPIVOT
(
Column123 FOR ColumnAll IN (Column1, Column2, Column3)
) AS unpvt;
https://www.w3schools.com/sql/sql_union.asp
https://www.mssqltips.com/sqlservertip/3000/use-sql-servers-unpivot-operator-to-help-normalize-output/
The answer is.. it depends..
If the number of columns are unknown.. then use unpivot as UZI has suggested
if you know all columns and is a small finite set..
you can simply go
Select
column1
from table
union all
select column2
from table
union all
select column3
from table
The Cartesian product of the T table with derived table of 3 rows.(each row of #t is presented 3 times, for а=1 and а=2 and а=3). For the first case we take value from Column1,
and for the second - from Column2 and for the Third - from Column3.
Here, certainly, there is both union and join but, in my opinion, the title's question means single scanning the table.
CREATE TABLE #t (Column1 NVARCHAR(25),Column2 NVARCHAR(25), column3 NVARCHAR(25))
INSERT INTO #t
SELECT '1','A','CAT'
SELECT
CASE a WHEN 1 THEN Column1 WHEN 2 THEN Column2 ELSE column3 END col
FROM #t, (SELECT 1 a UNION ALL SELECT 2 UNION ALL SELECT 3) B
DROP TABLE #t

Changing column in set update

Can I use a case statement in Set Column? I have multiple columns that need to be updated but the statement is quite similar. The only difference is what I'm selecting.
UPDATE TABLE1 A
SET A.COLUMN2 = (SELECT....
I want to update column2 to Column1 without repeating the same block of code.
Note: I'm using LISTAGG
Is there any way I could distinct both of the columns without trying to separate it in one query the make a subquery
I'm using this query and I know that listagg don't have the capabilities to distinct unless you distinct it first before using listagg
SELECT LISTAGG(COLUMN1 , ', ') WITHIN GROUP (ORDER BY COLUMN1) AS COLUMN1 ,
LISTAGG(COLUMN2 , ', ') WITHIN GROUP (ORDER BY COLUMN2) AS COLUMN1
FROM (SELECT COLUMN1 , COLUMN2 FROM TABLE2 B
WHERE A.COLUMN3 = B.COLUMN3
GROUP BY COLUMN1 , COLUMN2);
COLUMN1 COLUMN2
EGG PIE
EGG BREAD
Expected output
COLUMN1 COLUMN2
EGG PIE; BREAD
Do you mean
UPDATE table1
SET (column1, column2 ...) = (SELECT col1, col2 ...)

Delete reverse duplicate rows using sql

column1 column2
x y
y x
how does one go about eliminating such duplicates? or at worst selecting just one of those tuples?
It's become kind of a mainstream habit among question askers to withhold the information which RDBMS we are dealing with. In response: this is tested and works with a certain RDBMS I am not inclined to name. Go figure!
DELETE FROM tbl a
USING tbl b
WHERE (a.x, a.y) = (b.y, b.x)
AND a.y > a.x -- keep the one dupe with the biggest x
Assuming there are no dupes with x = y. This would be an ordinary duplicate anyway.
One approach is to identify only the valid rows e.g.
SELECT column1, column2
FROM T
WHERE column1 <= column2
UNION
SELECT column2 AS column1, column1 AS column2
FROM T
WHERE column1 > column2;
...then delete rows that aren't in the set of valid rows:
DELETE
FROM T
WHERE NOT EXISTS (
SELECT *
FROM (
SELECT column1, column2
FROM T
WHERE column1 <= column2
UNION
SELECT column2 AS column1, column1 AS column2
FROM T
WHERE column1 > column2
) AS DT1
WHERE DT1.column1 = T.column1
AND DT1.column2 = T.column2
);
Alternatively, the DELETE may be simplified to target only the invalid rows:
DELETE
FROM T
WHERE column1 > column2
AND EXISTS (
SELECT *
FROM T AS T1
WHERE T1.column1 = T.column2
AND T1.column2 = T.column1
);