select SQL rows depending on non-identical results of a query - sql

I am trying to select the columns which is relevant without knowing in advance which ones
I do a:
select *
from table
where id = '1'
the result i get is maybe 10 rows and 100+ columns
|id | column1 | column2 | column3 | column4 | column5 |....
| 1 | a | b | c | d | e |....
| 1 | a | XXX | c | d | e |....
| 1 | a | b | c | YYY | e |....
| 1 | a | b | c | d | e |....
For every row, one (or more) of the columns value is different, but i dont know which one(s)
is there any way i can create a temp table with the first query and do a sub query to display only one columns which doesnt have the same value in all the rows?
so the result would look like this:
|id | column2 | column4 |
| 1 | b | d |
| 1 | XXX | d |
| 1 | b | YYY |
| 1 | b | d |
since column 2 and 4 were the ones with non identical data these are the ones I want to see.

As already mentioned, this would require dynamic sql.
Maybe this will help you:
CREATE TABLE Column_Relevance
SELECT id,
COUNT(DISTINCT(column_1))/COUNT(*) AS relevance_column_1,
COUNT(DISTINCT(column_2))/COUNT(*) AS relevance_column_2,
COUNT(DISTINCT(column_3))/COUNT(*) AS relevance_column_3,
# AND SO ON....
GROUP BY id;
All relevance_columns with value < 1 indicate different values for the columns. You can build the whole statement in excel in a few minutes.
Once the table is created, add another column and create a select statement based on the column relevance (e.g. select if(relevance_column_1<1, column_1, else 'ignore') as column_1. This will return the string 'ignore' for all columns, that don't have distinct values.
This is far from perfect but maybe it helps you a little.

Here is a way you could use some aggregation to help. You said you have nearly 100 columns so this could take some effort to create but once it is done it would be fine. And this is just for analysis. You could utilize sys.columns to build the code for you but then we are back in the land of dynamic sql.
declare #Something table
(
ID int
, Column1 varchar(10)
, Column2 varchar(10)
, Column3 varchar(10)
, Column4 varchar(10)
, Column5 varchar(10)
)
insert #Something
values
(1, 'a', 'b', 'c', ' d ', 'e')
, (1, 'a', 'XXX', 'c', ' d ', 'e')
, (1, 'a', 'b', 'c', 'YYY', 'e')
, (1, 'a', 'b', 'c', ' d ', 'e')
;
with MinMax as
(
select ID
, MIN(Column1) as Col1Min
, MAX(Column1) as Col1Max
, MIN(Column2) as Col2Min
, MAX(Column2) as Col2Max
, MIN(Column3) as Col3Min
, MAX(Column3) as Col3Max
, MIN(Column4) as Col4Min
, MAX(Column4) as Col4Max
, MIN(Column5) as Col5Min
, MAX(Column5) as Col5Max
from #Something
group by ID
)
select s.ID
, Column1 = case when mm.Col1Max = mm.Col1Min then '' else s.Column1 end
, Column2 = case when mm.Col2Max = mm.Col2Min then '' else s.Column2 end
, Column3 = case when mm.Col3Max = mm.Col3Min then '' else s.Column3 end
, Column4 = case when mm.Col4Max = mm.Col4Min then '' else s.Column4 end
, Column5 = case when mm.Col5Max = mm.Col5Min then '' else s.Column5 end
from #Something s
join MinMax mm on mm.ID = s.ID

Have you tried using distinct ? It returns only unique rows:
select *
from table
where id = '1'
|id | column2 | column4 |
| 1 | a | a |
| 1 | a | a |
| 1 | b | d |
| 1 | b | d |
select distinct * from table where id= '1'
|id | column2 | column4 |
| 1 | a | a |
| 1 | b | d |
I hope this helps you.

Related

How to set Conditional Row Number by specific values from the table

I have a query that returns table and based on value (if that exists) I want to set row_number.
I have some solution but it looks long and I think could be easier and less code (best option) to handle it. Below sample with expected results:
If query returns Client with NULL:
----------------------
Process | Client|
A | NULL |
A | B |
A | B |
A | B |
A | C |
A | C |
A | C |
OutPut should be:
----------------------
Process | Client| RowNumber
A | NULL | 1
A | B | 2
A | B | 3
A | B | 4
A | C | 2
A | C | 3
A | C | 4
If query returns without NULL:
----------------------
Process | Client|
A | B |
A | B |
A | B |
A | C |
A | C |
A | C |
OutPut should be:
----------------------
Process | Client| RowNumber
A | B | 1
A | B | 2
A | B | 3
A | C | 1
A | C | 2
A | C | 3
I'm not sure if NULL should always be treated as 'B', but you need to handle that:
select t.* ,
row_number() over (partition by process, coalesce(client, 'B') order by (select null))
from t
where client is not null;
Oh, I see, you are not setting the NULL to 'B' but adding the number of NULLs to the other values. That is also quite simple:
select t.* ,
(row_number() over (partition by process, client order by (select null)) +
(case when client is null then 0
else sum(case when client is null then 1 else 0 end) over ()
end)
)
from t
where client is not null;
dbfiddle
DROP TABLE if exists mytable;
CREATE TABLE mytable(Process char(1), Client char(1));
INSERT INTO mytable values
('A',null),
('A','B'),
('A','B'),
('A','B'),
('A','C'),
('A','C'),
('A','C');
-- with a NULL value
select
Process,
Client,
ROW_NUMBER() OVER (partition by process,Client order by (select null))+CASE WHEN Client is null THEN 0 else 1 end R
from mytable;
-- without a NULL value
select
Process,
Client,
ROW_NUMBER() OVER (partition by process,Client order by (select null)) R
from mytable
where not client is null;
…
declare #t table(process varchar(10), client varchar(10));
insert into #t(process, client)
values
('A', null),
('A', 'B'),('A', 'B'),('A', 'B'),
('A', 'C'),('A', 'C'),
('A', ''), ('A', ''), ('A', ' '), ('A', ' '),
('A', 'ZXY'), ('A', 'ZXY'),
('X', 'B'),('X', 'B'),('X', 'B'),
('X', 'C'),('X', 'C');
select *,
row_number() over(partition by process,client order by client)
--if there is a null client per process then add 1 to every non null client
+ case when client is not null and min(case when client is null then 0 else 1 end) over(partition by process) = 0 then 1 else 0 end
--+ case when client is not null and min(isnull(ASCII(client+'.'), 0)) over(partition by process) = 0 then 1 else 0 end
as rownumber
from
(
select *
from #t
--where client is not null
) as t;

Select records within a group

Not sure how to frame this question so asking with an example.
From the below table, I want to find out all those records which are not defined with type as 'A'.
So from this table I want to find out the record with ID as 2.
TableA
+-----+------+
| ID1 | Type |
+-----+------+
| 1 | A |
| 1 | B |
| 1 | C |
| 2 | B |
| 2 | C |
| 3 | A |
| 3 | B |
| 3 | C |
+-----+------+
There is also a TableB, if we want to use.
+-----+
| ID2 |
+-----+
| 1 |
| 2 |
| 3 |
+-----+
Thanks a lot for helping.
One method is to use a HAVING clause with a conditional COUNT:
SELECT ID1
FROM dbo.YourTable
GROUP BY ID1
HAVING COUNT(CASE WHEN [Type] = 'A' THEN 1 END) = 0;
You can use a CTE to select all the IDs that are associated to the value you want to exclude.
Then you can use a subquery to filter out those IDs:
declare #TableA table (ID1 int, Type char(1))
insert into #TableA
values
(1, 'A')
,(1, 'B')
,(1, 'C')
,(2, 'B')
,(2, 'C')
,(3, 'A')
,(3, 'B')
,(3, 'C')
;with filteredIds
as
(
select
distinct ID1
from #TableA
where Type ='A'
)
select
*
from #TableA
where
ID1 not in (select id1 from filteredIds)
The result contains only the records that do not have value 'A' (in your example records with ID1=2):
You can use not exists:
select t2.id2
from TableB t2
where not exists (select 1
from TableA t1
where t2.id2 = t1.id1 and t1.Type = 'A'
);
With an index on TableA(ID1, Type), this is probably the fastest method under most circumstances.
Note that this also finds ids that are not in TableA at all.

how to select rows with same column_a but different column_b?

I want to select rows in sql server, there's my questions below:
Table1
--------------------------
| Name | Type |
--------------------------
| A | 1 |
| A | 2 |
| B | 1 |
| B | 3 |
| A | 3 |
| C | 1 |
| C | 3 |
| D | 1 |
| D | 2 |
| D | 3 |
| . | . |
| . | . |
Select rows like below:
Table2
--------------------------
| Name | Type |
--------------------------
| A | 1 |
| A | 2 |
| A | 3 |
| D | 1 |
| D | 2 |
| D | 3 |
| . | . |
| . | . |
The select rules is...
Show Name and Type which Type must have 1,2 and 3.
Example: A had 1,2,3 types,so i would select it.
Example: B only has 1,2 types,so i wouldn't select it.
You can use window functions for this:
select name, type
from (
select
t.*,
sum(case when type in (1, 2, 3) then 1 else 0 end)
over(partition by name) cnt
from mytable t
) t
where cnt = 3
This assumes that each (name, type) tuple occurs only once in the original table, which is consistant with your sample data.
Demo on DB Fiddle:
name | type
:--- | ---:
A | 1
A | 2
A | 3
D | 1
D | 2
D | 3
You could use INNER JOINs on the three Type columns to achieve this:
SELECT Table1.[Name],
Table1.[Type]
FROM Table1
INNER JOIN (
SELECT [Name]
FROM Table1
WHERE ([Type] = 1)
) A ON A.[Name] = Table1.[Name]
INNER JOIN (
SELECT [Name]
FROM Table1
WHERE ([Type] = 2)
) B ON B.[Name] = A.[Name]
INNER JOIN (
SELECT [Name]
FROM Table1
WHERE ([Type] = 3)
) C ON C.[Name] = A.[Name]
This outputs:
Name Type
A 1
A 2
A 3
D 1
D 2
D 3
The matching sqlfiddle.
This works by returning rows that contain [Type] = 1, and then ONLY matching rows where [Type] = 2 and [Type] = 3. Then this is joined back to your main table and the results are returned.
Get the names with group by name and set the condition in the having clause:
select * from Table1
where name in (
select name
from Table1
group by name
having count(distinct type) = 3
)
If there are for the column Type other values than 1, 2, 3 then:
select * from Table1
where type in (1, 2, 3) and name in (
select name
from Table1
where type in (1, 2, 3)
group by name
having count(distinct type) = 3
)
See the demo.
Results:
> Name | Type
> :--- | ---:
> A | 1
> A | 2
> A | 3
> D | 1
> D | 2
> D | 3
you can use string_agg if it is sql server 2017 and above or Azure SQL as below:
Select * from #yourTable yt join (
select [name], string_agg([Type], ',') as st_types
from #YourTable
group by [name] ) a
on yt.name = a.[name] and a.st_types like '%1,2,3%'
I give you this, this will work if you have:
A 1
A 2
A 3
A 2
It will then only give you B.
SELECT *
FROM Table1
WHERE Name in (
SELECT Name from
(
SELECT Name, Type, count(Name) c from Table1 where Type = 1
GROUP BY Name, Type
HAVING count(Name) = 1
UNION
SELECT Name, Type, count(Name) c from Table1 where Type = 2
GROUP by Name, Type
HAVING count(Name) = 1
UNION
SELECT Name, Type, count(Name) c from Table1 where Type = 3
GROUP by Name, Type
HAVING count(Name) = 1) t
GROUP by name
HAVING count(c) = 3)
Here is the DEMO

How to split two columns into two rows using oracle sql?

for example I have a data below:
column1 column2 column3 column4 column5
A B 5 2 3
My desired table would be:
columnA
A
B
The condition to determine the splitting is if (column3 + column4)>5 otherwise no splitting.
union all:
select column1 as columnA from t where column3 + column4 > 5
union all
select column2 from t where column3 + column4 > 5 ;
I'm not sure I fully understood what you are looking for, but if you wan' to put a if, ele in the SELECT, you might wan't to use the CASE expression.
https://docs.oracle.com/cd/B19306_01/server.102/b14200/expressions004.htm
Use a LEFT JOIN (with join condition) or CROSS JOIN (no join condition) to a derived table that consists of 2 rows, then output the values via a case expression. Both approaches allow access to all columns for other conditions too.
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE TABLE1
(COLUMN1 VARCHAR2(1), COLUMN2 VARCHAR2(1), COLUMN3 INT, COLUMN4 INT, COLUMN5 INT)
;
INSERT ALL
INTO TABLE1 (COLUMN1, COLUMN2, COLUMN3, COLUMN4, COLUMN5)
VALUES ('A', 'B', 5, 2, 3)
INTO TABLE1 (COLUMN1, COLUMN2, COLUMN3, COLUMN4, COLUMN5)
VALUES ('C', 'D', 1, 1, 1)
SELECT * FROM DUAL
Query 1:
select
case when lj.rn = 1 then t.column1 else t.column2 end as columnA
, t.column1
, t.column2
, t.column3
, t.column4
, t.column5
from table1 t
left join ( select 1 as rn from dual union all select 2 from dual ) lj
on (column3 + column4) > 5
Results:
| COLUMNA | COLUMN1 | COLUMN2 | COLUMN3 | COLUMN4 | COLUMN5 |
|---------|---------|---------|---------|---------|---------|
| A | A | B | 5 | 2 | 3 |
| B | A | B | 5 | 2 | 3 |
| D | C | D | 1 | 1 | 1 |
Query 2:
select
case when cj.rn = 1 then t.column1 else t.column2 end as columnA
, t.column1
, t.column2
, t.column3
, t.column4
, t.column5
from table1 t
cross join ( select 1 as rn from dual union all select 2 from dual ) cj
Results:
| COLUMNA | COLUMN1 | COLUMN2 | COLUMN3 | COLUMN4 | COLUMN5 |
|---------|---------|---------|---------|---------|---------|
| A | A | B | 5 | 2 | 3 |
| B | A | B | 5 | 2 | 3 |
| C | C | D | 1 | 1 | 1 |
| D | C | D | 1 | 1 | 1 |
Suppose I have table test with sample data
column1 | column2 | column3 | column4 | column5
-------------------------------------------------------
A | B | 5 | 2 | 3
C | D | 1 | 1 | 1
E | F | 4 | 5 | 1
Script for above sample data:-
CREATE TABLE TEST
(
column1 VARCHAR2(10),
column2 VARCHAR2(10),
column3 NUMBER(2),
column4 NUMBER(2),
column5 NUMBER(2)
);
INSERT INTO TEST VALUES('A','B',5,2,3);
INSERT INTO TEST VALUES('C','D',1,1,1);
INSERT INTO TEST VALUES('E','F',4,5,1);
COMMIT;
Below query will give required output:-
WITH tmp AS
(SELECT (CASE
WHEN column3 + column4 > 5 THEN
column1||','||column2
ELSE
NULL
END) columna,
column1,column2,column3,column4,column5
FROM TEST)
SELECT regexp_substr(columna,'[^,]+',1,LEVEL) columna,
column1,column2,column3,column4,column5
FROM tmp
CONNECT BY LEVEL <= regexp_count(columna, ',') + 1
AND PRIOR column1 = column1
AND PRIOR dbms_random.random IS NOT NULL;
Output:
columnA | column1 | column2 | column3 | column4 | column5
-------------------------------------------------------------------
A | A | B | 5 | 2 | 3
B | A | B | 5 | 2 | 3
| C | D | 1 | 1 | 1
E | E | F | 4 | 5 | 1
F | E | F | 4 | 5 | 1

SQL: Inner join with top 2 rows with second table from on condition

Hi I am now trying to join 2 table with only 2 rows from second table join to first table.
For example, I have following 2 tables:
**Table A**
Column1 | Column2 | Column3
A | B | 30
A | C | 50
A | D | 25
**Table B**
Column4 | Column5
B | 35
B | 90
B | 65
B | 80
B | 85
B | 40
C | 100
C | 60
C | 70
C | 65
Here is example of my normal query:
select *
from
(
select *
from A
where Column1 = 'A' and (Column2 = 'B' or Column2 = 'C')
order by Column2, Column3
) A
inner join
(
select *
from B
where (Column4 = 'B' or Column4 = 'C')
order by Column5
) B
on (A.Column2 = B.Column4 and ((B.Column5 - A.Column3) > 30))
The Result should look like:
**Result:**
Column1 | Column2 | Column3 | Column4 | Column5
A | B | 30 | B | 65
A | B | 30 | B | 80
A | B | 30 | B | 85
A | B | 30 | B | 90
A | C | 50 | C | 100
However, the result that I want is to join only 2 row from second table result only. The expected result should be:
**Expected Result:**
Column1 | Column2 | Column3 | Column4 | Column5
A | B | 30 | B | 65
A | B | 30 | B | 80
A | C | 50 | C | 100
Do anyone have idea of how to create such sql statement? Thank you.
You could use row_number() to limit the number of rows. The example assumes that (Column1, Column2, Column3) is unique. If table A has a primary key, use that instead.
select *
from (
select Column1
, Column2
, Column3
, Column4
, Column5
, row_number() over (partition by Column1, Column2, Column3
order by Column5 - Column3 desc) as rn
from A
join B
on A.Column2 = B.Column4
where Column1 = 'A'
and Column2 in ('B', 'C')
and Column5 - Column3 > 30
) SubQueryAlias
where rn < 2
See example at SQL Fiddle.
A good start would be to write more simple SQL without the inline views:
select *
from A inner join
B on (A.Column2 = B.Column4)
where A.Column1 = 'A' and
A.Column2 in ('B','C') and
(B.Column5 - A.Column3) > 30)
Try using a CTE
WITH Top2Rows AS (
SELECT TOP 2 *
FROM TableB
WHERE ([ADD CONSTRAINTS])
)
SELECT *
FROM TableA a
JOIN Top2Rows r ON a.ID = b.ID
WHERE ([ADD CONSTRAINTS])