SQL help on count in certain ID - sql

Do you know how to display only the lines in table for same ID where col3 is not 'X'?
e.g., in the following table, it should display only ID 2 (as all the col2 are null)
ID | col1 | col2 | col3
---+------+------+-----
1 | 0 | 0 | X
1 | D | C | null
1 | D | C | null
2 | 0 | 0 | null
2 | D | C | null
2 | D | C | null
It should work for all ID with some many line by ID and only the same ID with all line having null.

If you are looking to get records where ID does not have at least one X in col 3 for other records:
SELECT Y.*
FROM Your_Table Y
WHERE Y.ID NOT IN (SELECT X.ID FROM YOUR_TABLE X WHERE X.ID=Y.ID AND X.COL3='X')

Most DBMS support 3 valued logic - True, False, and Undefined. NULL <> 3 is undefined, since NULL is an unknown value. You need to handle NULLs explicitly.
SELECT *
FROM Your_Table
WHERE col3 <> X
OR col3 IS NULL;

select * from table
where (col1 = col2) and (col3 <> 'X')

Use window functions or not exists:
select t.*
from t
where not exists (select 1 from t t2 where t2.id = t.id and t2.col3 = 'X');

Related

sql - Only want rows with NULL in column if it isn't defined somewhere else as well

I have a table with possible NULL values in a column. I need to return the NULL values, but only if it isn't also defined somewhere else. Below, I want row F, but I do not want row B. We have some automation that attempts something but also has a fail over. We need to identify when both tries fail.
Column 1 | Column 2
A | 1
B | 1
B | null
C | 2
C | 1
D | 1
E | 2
F | null
F | null
G | 2
Simply do aggregation :
select col1, null as col2
from table t
group by col1
having max(col2) is null;
You can use not exists:
select t.*
from mytable t
where not exists (
select 1
from mytable t1
where t1.column1 = t.column1 and t1.column2 is not null
)
Or you can use window functions:
select column1, column2
from (
select t.*, max(column2) over(partition by column1) max_column2
from mytable t
) t
where max_column2 is null

Fetch duplicate rows from a table

I have the following table
Col1 | Col2
2 | jim
2 | jam
3 | raw
3 | cooked
3 | boiled
5 | none
6 | yum
So in this table I want to fetch records which have multiple value in col1 like:
Col1 | Col2
2 | jim
2 | jam
3 | raw
3 | cooked
3 | boiled
This should work for you, an alternative to using EXISTS:
select t.*
from <table> t
cross apply (select 1 ex
from <table> t2
where t2.Col1=t.Col1
group by t2.Col1
having count(t2.Col1) > 1) tmp
Use exists:
select col1, col2
from t
where exists (select 1
from t t2
where t2.col1 = t.col1 and t2.col2 <> t.col2
);
Use this query
select *
from t
where col1 in
( select col1
from t
group by col1
having count(*) > 1
)

How to Update the Following Table with MERGE in Oracle?

I have the following data set (Oracle 12):
Table X
+---------+--------+---------------+--------+
| COLN | COLM | COLK | COLP |
+---------+--------+---------------+--------+
| 1 | 500 | K1 | 777 |
+---------+--------+---------------+--------+
Table A
+---------+--------+---------------+--------+
| COL1 | COL2 | COL3 | COL4 |
+---------+--------+---------------+--------+
| 1 | K1 | 500 | B |
| 1 | K2 | 500 | NULL |
+---------+--------+---------------+--------+
Table B
+---------+--------+---------+
| COLZ | COLX | COLW |
+---------+--------+---------+
| 1 | K1 | 777 |
| 1 | K2 | 678 |
+---------+--------+---------+
The three tables have the following commonality:
X.COLN = A.COL1 = B.COLZ
X.COLk = A.COL2 = B.COLX
X.COLM = A.COL3
I need to write a query which retrieves values for the following columns in one query:
X.COLK, X.COLP, B.COLX, B.COLW
The ultimate goal is, if the following conditions are met:
If there more than one record in Table A where A.COL1's and A.COL3's are matching (and there is a corresponding record in Table X)
And one of the rows is not null, e.g. A.COL4 = B, and another one is NULL
I update Table X to replace X.COLK, X.COLP (K1 and 777) in my MERGE statement with values in Table B (B.COLX, B.COLW -- K2 and 678).
Is this possible?
MERGE INTO X FX
USING (
SELECT COLX ONGOING_X, COLW ONGOING_W
FROM B
WHERE (COLZ, COLX) IN
(SELECT COL1, COL2
FROM A
WHERE COL3 = ?
AND COL1 = ?
AND COL4 IS NULL)
) NEW_B
ON (FX.COLk = ?
AND FX.COLP = ?)
WHEN MATCHED THEN
UPDATE SET
FX.COLk = NEW_B.ONGOING_X,
FX.FOLP = NEW_B.ONGOING_W;
You may do a MERGE using ROWID.
MERGE INTO x tgt USING (
WITH c AS (
SELECT col1,
col3,
MAX(
CASE
WHEN col4 IS NULL THEN col2
END
) AS col2 --Ongoing col2 as indicated from col4
FROM a
GROUP BY col1,
col3
HAVING COUNT(
CASE
WHEN col4 IS NULL THEN 1
END
) = 1 AND COUNT(col4) = 1 --Contains one and exactly one NULL and one NON NULL
) SELECT x.rowid AS rid,
b.*
FROM x
JOIN c ON c.col1 = x.coln AND c.col3 = x.colm
JOIN b ON b.colz = c.col1 AND b.colx = c.col2 --Join with ongoing value from c( a.k.a table A )
)
src ON ( tgt.rowid = src.rid ) --ROWID match
WHEN MATCHED THEN UPDATE SET tgt.colk = src.colx,
tgt.colp = src.colw;
Demo

Iterating through every row efficient?

Suppose I have the following table T1:
| col1 | col2 |
|------|------|
| 0 | 0 | // A++
| 3 | 123 | // B++
| 0 | 5 | // C++
| 8 | 432 | // A++
| 0 | 4 | // B++
I now need to create a trigger (on INSERT), that analyses every row, increases a counter (see below), populates the table T2 with the values of the counter:
IF col1 = 0 AND col2 = 0
A++
ELSE IF col1 = 0 col2 > 0
B++
ELSE IF col1 > 0
C++
In this case, T2 would look like:
| id | A | B | C |
|----|---|---|---|
| 1 | 1 | 2 | 2 |
My question is more about the design: Should I really iterate through each row, as described HERE, or is there a more efficient way?
Try something like this in trigger
;with data as
(
SELECT Sum(CASE WHEN col1 = 0 AND col2 = 0 THEN 1 END) AS a,
Sum(CASE WHEN col1 = 0 AND col2 > 0 THEN 1 END) AS b,
Sum(CASE WHEN col1 > 0 THEN 1 END) AS c
FROM (VALUES (0, 0 ),
(3, 123 ),
(0, 5 ),
(8, 432 ),
(0, 4 ) ) tc ( col1, col2 )
)
UPDATE yt
SET a = dt.a,
b = dt.b,
c = dt.c
FROM yourtable yt
JOIN data dt
ON a.id = b.id
This does not require row by row iteration. Replace the table valued constructor with Inserted table
This is something you should not write into a table (unless there aren't millions of rows and you need this for performance...). You should rather get this information on-the-fly like this:
DECLARE #T1 TABLE(col1 INT,col2 INT);
INSERT INTO #T1(col1,col2) VALUES
(0,0)
,(3,123)
,(0,5)
,(8,432)
,(0,4);
SELECT p.*
FROM
(
SELECT CASE WHEN col1=0 AND col2=0 THEN 'A'
WHEN col1=0 AND col2>0 THEN 'B'
WHEN col1>0 THEN 'C' END AS Category
FROM #T1 AS t
) AS tbl
PIVOT
(
COUNT(Category) FOR Category IN(A,B,C)
) AS p
The result
A B C
1 2 2
And I would suggest you to add another option (with ELSE) to catch invalid data (e.g. negativ values).

Update unique rows in SQL

I have a table
id | col1 | col3| col4
1 | x | r |
2 | y | m |
3 | z | p |
4 | x | r |
i have to update all unique rows of this table
i.e
id | col1 | col3| col4
1 | x | r | 1
2 | y | m | 1
3 | z | p | 1
4 | x | r | 0
i can fetch unique rows by
select distinct col1,col2 from table
.But how can i identify these rows in order to update them.Please help.
You can use the group by to pick unique result:
SELECT MIN(ID) AS ID FROM TABLE GROUP BY COL1, COL3;
id | col1 | col3
1 | x | r
2 | y | m
3 | z | p
Then
UPDATE TABLE SET col4 = 1 WHERE ID IN (SELECT MIN(ID) FROM TABLE GROUP BY COL1, COL3);
Restriction is that the id column should be unique.
If it is a small enough table, here is what you can do
Step 1: Update everything to 1
Update Table Set Col4 = 1
Step 2: Update all dups to 0 (OTTOMH)
Update Table
Set Col4 = 0
From
(
Select Col1, Min (Id) FirstId
From Table
Group By Col1
Having Count (*) > 1
) Duplicates
Where Table.Col1 = Duplicates.Col1
And Table.Id <> Duplicates.FirstId
You can also try:
UPDATE test
SET col4 = 1
WHERE id IN
(
SELECT t1.id
FROM table_name t1
LEFT JOIN table_name t2
ON t2.id < t1.id
AND t2.col1 = t1.col1
AND t2.col3 = t1.col3
WHERE t2.id IS NULL
)
One more slightly convoluted option, to set both 0 and 1 values in one hit:
update my_table mt
set col4 = (
select case when rn = 1 then 1 else 0 end
from (
select id,
row_number() over (partition by col1, col3 order by id) as rn
from my_table) tt
where tt.id = mt.id);
4 rows updated.
select * from my_table order by id;
ID COL1 COL3 COL4
---------- ---- ---- ----------
1 x r 1
2 y m 1
3 z p 1
4 x r 0
This is just using row_number() to decide which of the unique combinations is first, arbitrarily using the lowest id, assigning that the value of one, and everything else zero.