SQL query to get rows from two tables - sql

I have two tables with ID and value columns.
I want to union these two tables but if ID exists in second table, I want to discard all same IDs in first table and retrieve only these IDs from second table. How can I create the query for this?
First table:
ID Value
100 1
100 2
101 3
102 4
Second table:
ID Value
100 5
100 6
100 7
102 5
The result I want to achieve:
ID Value
100 5
100 6
100 7
101 3
102 5
I tried to do as suggested but it still returns only values from table 1:
String selectQuery = "SELECT * FROM " + TABLE1_NAME
+" UNION ALL"
+" SELECT * FROM " + TABLE2_NAME
+" WHERE "+id+" NOT IN (SELECT "+id+" FROM "+TABLE2_NAME+ ")";

try
select id,value from table1
union ALL
select id , value from table2
where id not in (select id from table1)
edit as suggested by Ormoz :
To use table2's result if both tables have the ids :
select id,value from table2
union ALL
select id , value from table1
where id not in (select id from table2)
My tests:
create table table1 (id int not null, value int not null);
insert into table1 values
(100, 1),
(100, 2),
(101, 3),
(102, 4);
create table table2 (id int not null, value int not null);
insert into table2 values
(100, 5),
(100, 6),
(100, 7),
(102, 5);
select id,value , 't2' as t from table2
union ALL
select id , value, 't1' from table1
where id not in (select id from table2);
this is the output:
# id, value, t
'100', '5', 't2'
'100', '6', 't2'
'100', '7', 't2'
'102', '5', 't2'
'101', '3', 't1'

Maybe this could do the trick?
(
SELECT id, value FROM table2
UNION ALL
SELECT id, value FROM table1
) GROUP BY id

My problem is solved by removing the where clause to first select:
String selectQuery = "SELECT * FROM " + TABLE1_NAME
+" WHERE "+id+" NOT IN (SELECT "+id+" FROM "+TABLE2_NAME)
+" UNION ALL"
+" SELECT * FROM " + TABLE2_NAME;
Special thanks to #Tim3880 and #Ormoz =)

Related

Check if Active Inactive date lies within the Date Range of another Active Inactive Date

Table 1
Loc_Id
Label_Id
Active_Date
Inactive_Date
1
1001
2022/05/13
9999/12/31
2
1001
2018/05/20
2022/05/12
3
1001
2012/06/14
2018/05/12
Table 2
Label_Id
Tab2_Active_Date
Tab2_Inactive_Date
1001
2022/05/13
9999/12/31
1001
2018/05/22
2022/05/12
1001
2012/06/14
2018/05/12
I want to know which records in Table2 have Tab2_Active Date > Active Date in Table 1 and Tab2_Inactive Date < Inactive Date in Table 1.
For example in this the scenario the date Tab2_Active Date 2018/05/22 mentioned in Table 2 is greater than 2018/05/20 mentioned in table 1.
So the o/p will be
Loc_Id
Tab2_Active_Date
Tab2_Inactive_Date
2
2018/05/22
2022/05/12
Since I only have only Ids to join as the keys for 2 tables and I need to compare the dates, I cannot take dates to join the tables which results in inaccurate data.
Create table #T1
(
Loc_Id int,
Label_Id int,
Active_Date date,
Inactive_Date date
)
Create table #T2
(
Label_Id int,
Active_Date date,
Inactive_Date date
)
Insert into #T1
Select 1, 1001, '2022-05-13', '9999-12-31'
union
Select 2, 1001, '2022-05-20', '2022-05-12'
union
Select 3, 1001, '2022-06-14', '2018-05-12'
union
Select 4, 1001, '2022-07-14', '2018-08-13'
Insert into #T2
Select 1001, '2022-05-13', '9999-12-31'
union
Select 1001, '2022-05-22', '2022-05-12'
union
Select 1001, '2022-06-14', '2018-05-12'
union
Select 1001, '2022-06-14', '2018-05-12'
union
Select 1001, '2022-07-14', '2018-08-12'
;with Cte as
(
Select Label_Id, Active_Date, Inactive_Date from #T2
EXCEPT
Select Label_Id, Active_Date, Inactive_Date from #T1
)
Select t1.Loc_Id, t2.Active_Date, t2.Inactive_Date
from #T1 t1
inner join Cte t2 on t1.Label_Id = t2.Label_Id and (t2.Active_Date > t1.Active_Date and t2.Inactive_Date = t1.Inactive_Date)
union
Select t1.Loc_Id, t2.Active_Date, t2.Inactive_Date
from #T1 t1
inner join Cte t2 on t1.Label_Id = t2.Label_Id and (t2.Inactive_Date < t1.Inactive_Date and t2.Active_Date = t1.Active_Date)
Drop table #T1
Drop table #T2
Here's what I came up with
with t1 as (
select * from (values
(1, 1001, '2022-05-13', '9999-12-31'),
(2, 1001, '2018-05-20', '2022-05-12'),
(3, 1001, '2012-06-14', '2018-05-12')
) as x(Loc_ID, Label_Id, Active_Date, Inactive_Date)
),
t2 as (
select * from (values
(1001, '2022-05-13', '9999-12-31'),
(1001, '2018-05-22', '2022-05-12'),
(1001, '2012-06-14', '2018-05-12')
) as x(Label_Id, Active_Date, Inactive_Date)
)
select t1.*, '||', t2.*
from t1
join t2
on t2.Active_Date >= t1.Active_Date
and t2.Inactive_Date <= t1.Inactive_Date
and (
t1.Active_Date <> t2.Active_Date
or t1.Inactive_Date <> t2.Inactive_Date
)
Ignoring the CTEs (that's just a way to get the data into a tabular structure), the join criteria in the SELECT statement say that there must be partial overlap in the interval (which are the first two predicates on only one of active_date or inactive_date) but not complete overlap (which is the compound predicate saying that at least one of active_date or inactive_date must not match).

SQL: select with disctint left

table
id name data
---------------
1 name1 data1
2 name2 data2
3 name3 data3
4 name4 data1abc
5 name5 data2abc
6 name6 data1abcd
7 name7 data2abcde
The output that I need is rows with ids 3, 6 and 7.
I need to search for distinct data terms. The terms data1, data1abc, data1abcd should all be counted as one term and the rows with unique most characters should be returned i.e., data1abcd, data2abcde, data3
Can you help please?
This is what I have written so far, it doesnt work:
SELECT *
FROM table
WHERE LEFT(data, 5) = (
SELECT distinct LEFT(data, 5)
FROM table
)
This will work for any string length:
select s
from (
select
s,
case
when s = left(lag(s) over (order by s desc), length(s))
then false else true
end as u
from t
) t
where u
order by s
;
s
------------
data1abcd
data2abcde
data3
Sample data:
create table t (id int, name text, s text);
insert into t (id, name, s) values
(1, 'name1', 'data1'),
(2, 'name2', 'data2'),
(3, 'name3', 'data3'),
(4, 'name4', 'data1abc'),
(5, 'name5', 'data2abc'),
(6, 'name6', 'data1abcd'),
(7, 'name7', 'data2abcde');
You could partition the rows by the first five characters and take the longest:
SELECT name, data
FROM (SELECT name,
data,
RANK() OVER (PARTITION BY LEFT(data, 5)
ORDER BY LENGTH(data) DESC) AS rk
FROM mytable) t
WHERE rk = 1
Note:
The rank function allows for ties (e.g., data1a and data1b have the same length, so they could both have the rank of 1 if there a no longer strings). If you do not want to allow for ties, you should use row_number instead.
It seems that you want distinct non-numeric values. You may change data_len as len(data_r) if you want the longest non-numeric records
ALTER TABLE <yourtable>
ADD
data_r as REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE (data, '0', ''),'1', ''),'2', ''),'3', ''),'4', ''),'5', ''),'6', ''),'7', ''),'8', ''),'9', '')
,data_len as len(data)
SELECT * FROM <yourtable> t
WHERE EXISTS ( SELECT MAX(data_len), data_r
FROM <yourtable> t1
WHERE t.data_r = t1.data_r
AND t.data_len = t1.data_len
GROUP BY data_r
)

Update column based on IF Else Condition

I have two tables A and B
Table A
ID_number as PK
first_name,
L_Name
Table B
ID_number,
Email_id,
Flag
I have several people who have multiple email ID and are already flagged as X on table B.
Whereas i am trying to find list of people who have an email id or multiple email ID, but were never flagged.
e.g John clark might have 2 email in table B, but was never flagged.
Simply use not exists:
select a.*
from a
where not exists (select 1
from b
where b.id_number = a.id_number and b.flag = 'X'
);
You may want to perform an update, but your question seems to be only about selecting (probably to update based on select). It should be something like this:
SELECT A.L_Name
FROM A
WHERE NOT EXISTS (
SELECT 1
FROM B
WHERE B.ID_number = A.ID_number AND B.Flag = 'X'
)
OR the LEFT JOIN version
SELECT 1
FROM A
LEFT JOIN B ON B.ID_number = A.ID_number AND B.Flag = 'X'
WHER B.ID_number IS NULL
Usually, the first version is faster than the second one.
Forget Table A...
SELECT DISTINCT ID_number FROM table_b t1
WHERE NOT EXISTS(
SELECT NULL FROM table_b t2 WHERE t1.ID_number=t2.ID_number AND t2.flag='X'
)
Judging by your responses in the comments, I believe this is what you are looking for:
--drop table update_test;
create table update_test
(
id_num number,
email_id number,
flag varchar2(1) default null
);
insert into update_test values (1, 1, null);
insert into update_test values (1, 2, null);
insert into update_test values (2, 3, null);
insert into update_test values (2, 7, null);
insert into update_test values (3, 2, null);
insert into update_test values (3, 3, 'X');
insert into update_test values (3, 7, null);
select * from update_test;
select id_num, min(email_id)
from update_test
group by id_num;
update update_test ut1
set flag = case
when email_id = (
select min(email_id)
from update_test ut2
where ut2.id_num = ut1.id_num
) then 'X'
else null end
where id_num not in (
select id_num
from update_test
where Flag is not null);
The last update statement will update and set the Flag field on the record for each id_num group with the lowest email_id. If the id_num group already has the Flag field set for one it will ignore it.

Joining a list of values with table rows in SQL

Suppose I have a list of values, such as 1, 2, 3, 4, 5 and a table where some of those values exist in some column. Here is an example:
id name
1 Alice
3 Cindy
5 Elmore
6 Felix
I want to create a SELECT statement that will include all of the values from my list as well as the information from those rows that match the values, i.e., perform a LEFT OUTER JOIN between my list and the table, so the result would be like follows:
id name
1 Alice
2 (null)
3 Cindy
4 (null)
5 Elmore
How do I do that without creating a temp table or using multiple UNION operators?
If in Microsoft SQL Server 2008 or later, then you can use Table Value Constructor
Select v.valueId, m.name
From (values (1), (2), (3), (4), (5)) v(valueId)
left Join otherTable m
on m.id = v.valueId
Postgres also has this construction VALUES Lists:
SELECT * FROM (VALUES (1, 'one'), (2, 'two'), (3, 'three')) AS t (num,letter)
Also note the possible Common Table Expression syntax which can be handy to make joins:
WITH my_values(num, str) AS (
VALUES (1, 'one'), (2, 'two'), (3, 'three')
)
SELECT num, txt FROM my_values
With Oracle it's possible, though heavier From ASK TOM:
with id_list as (
select 10 id from dual union all
select 20 id from dual union all
select 25 id from dual union all
select 70 id from dual union all
select 90 id from dual
)
select * from id_list;
the following solution for oracle is adopted from this source. the basic idea is to exploit oracle's hierarchical queries. you have to specify a maximum length of the list (100 in the sample query below).
select d.lstid
, t.name
from (
select substr(
csv
, instr(csv,',',1,lev) + 1
, instr(csv,',',1,lev+1 )-instr(csv,',',1,lev)-1
) lstid
from (select ','||'1,2,3,4,5'||',' csv from dual)
, (select level lev from dual connect by level <= 100)
where lev <= length(csv)-length(replace(csv,','))-1
) d
left join test t on ( d.lstid = t.id )
;
check out this sql fiddle to see it work.
Bit late on this, but for Oracle you could do something like this to get a table of values:
SELECT rownum + 5 /*start*/ - 1 as myval
FROM dual
CONNECT BY LEVEL <= 100 /*end*/ - 5 /*start*/ + 1
... And then join that to your table:
SELECT *
FROM
(SELECT rownum + 1 /*start*/ - 1 myval
FROM dual
CONNECT BY LEVEL <= 5 /*end*/ - 1 /*start*/ + 1) mypseudotable
left outer join myothertable
on mypseudotable.myval = myothertable.correspondingval
Assuming myTable is the name of your table, following code should work.
;with x as
(
select top (select max(id) from [myTable]) number from [master]..spt_values
),
y as
(select row_number() over (order by x.number) as id
from x)
select y.id, t.name
from y left join myTable as t
on y.id = t.id;
Caution: This is SQL Server implementation.
fiddle
For getting sequential numbers as required for part of output (This method eliminates values to type for n numbers):
declare #site as int
set #site = 1
while #site<=200
begin
insert into ##table
values (#site)
set #site=#site+1
end
Final output[post above step]:
select * from ##table
select v.id,m.name from ##table as v
left outer join [source_table] m
on m.id=v.id
Suppose your table that has values 1,2,3,4,5 is named list_of_values, and suppose the table that contain some values but has the name column as some_values, you can do:
SELECT B.id,A.name
FROM [list_of_values] AS B
LEFT JOIN [some_values] AS A
ON B.ID = A.ID

Inserting missing rows with a join

I have a SQL script that returns this derived table.
MM/YYYY Cat Score
01/2012 Test1 17
02/2012 Test1 19
04/2012 Test1 15
05/2012 Test1 16
07/2012 Test1 14
08/2012 Test1 15
09/2012 Test1 15
12/2012 Test1 11
01/2013 Test2 10
02/2013 Test2 15
03/2013 Test2 13
05/2013 Test2 18
06/2013 Test2 14
08/2013 Test2 15
09/2013 Test2 14
12/2013 Test2 10
As you can see, I am missing some MM/YYYYs (03/2012, 06/2012, 11/2012, etc).
I would like to fill in the missing MM/YYYYs with the Cat & a 0 (zero) form the score.
I have tried to join a table that contains the all MM/YYYY for the ranges the query will be run, but this only returns the missing rows for the first occurrence, it does not repeat for each Cat (should have known that).
So my question is this, can I do this using a join or will I have to do this in a temp table, and then output the data.
AHIGA,
LarryR…
You need to cross join your categories and a list of all dates in the range. Since you have posted no table structures I'll have to guess at your structure slightly, but assuming you have a calendar table you can use something like this:
SELECT calendar.Date,
Category.Cat,
Score = ISNULL(Scores.Score, 0)
FROM Calendar
CROSS JOIN Catogory
LEFT JOIN Scores
ON Scores.Cat = Category.Cat
AND Scores.Date = Calendar.Date
WHERE Calendar.DayOfMonth = 1;
If you do not have a calendar table you can generate a list of dates using the system table Master..spt_values:
SELECT Date = DATEADD(MONTH, Number, '20120101')
FROM Master..spt_values
WHERE Type = 'P';
Where the hardcoded date '20120101' is the first date in your range.
ADDENDUM
If you need to actually insert the missing rows, rather than just have a query that fills in the blanks you can use this:
INSERT Scores (Date, Cat, Score)
SELECT calendar.Date,
Category.Cat,
Score = 0
FROM Calendar
CROSS JOIN Catogory
WHERE Calendar.DayOfMonth = 1
AND NOT EXISTS
( SELECT 1
FROM Scores
WHERE Scores.Cat = Category.Cat
AND Scores.Date = Calendar.Date
)
Although, in my opinion if you have a query that fills in the blanks inserting the data is a bit of a waste of time.
To get what you want, start with a driver table and then use left outer join. The result is something like this:
select driver.cat, driver.MMYYYY, coalesce(t.score, 0) as score
from (select cat, MMYYYY
from (select distinct cat from t) c cross join
themonths -- use where to get a date range
) driver left outer join
t
on t.cat = driver.cat and t.MMMYYYY = driver.MMYYYY
Try this one -
DECLARE #temp TABLE (FDOM DATETIME, Cat NVARCHAR(50), Score INT)
INSERT INTO #temp (FDOM, Cat, Score)
VALUES
('20120101', 'Test1', 17),('20120201', 'Test1', 19),
('20120401', 'Test1', 15),('20120501', 'Test1', 16),
('20120701', 'Test1', 14),('20120801', 'Test1', 15),
('20120901', 'Test1', 15),('20121001', 'Test1', 13),
('20121201', 'Test1', 11),('20130101', 'Test1', 10),
('20130201', 'Test1', 15),('20130301', 'Test1', 13),
('20130501', 'Test1', 18),('20130601', 'Test1', 14),
('20130801', 'Test1', 15),('20130901', 'Test1', 14),
('20131201', 'Test1', 10),('20120601', 'Test2', 10)
;WITH enum AS
(
SELECT Cat, StartDate = MIN(FDOM), EndDate = MAX(FDOM)
FROM #temp
GROUP BY Cat
UNION ALL
SELECT Cat, DATEADD(MONTH, 1, StartDate), EndDate
FROM enum
WHERE StartDate < EndDate
)
SELECT e.StartDate, t.Cat, Score = ISNULL(t.Score, 0)
FROM enum e
LEFT JOIN #temp t ON e.StartDate = t.FDOM AND e.Cat = t.Cat
ORDER BY e.StartDate, t.Cat
Do a left join from "complete table" to "incomplete table" and set a where statement to check the date column of the "incomplete" table. So you will only get the missing results in your select query. After that, just set a "insert into tablename" before.
In the first run it will find two rows, that aren't already in the incomplete table. So it will be inserted by the insert into statement, two rows affected. In a second run the result in the select statement has 0 rows, so nothing happens. Zero rows affected :-)
Sample: http://sqlfiddle.com/#!2/895fe/6
(Just mark the select statement; the insert into statement isn't required to just see, how the join works)
Insert Into supportContacts
Select * FROM
(
Select
'01/2012' as DDate, 'Test1' as Cat, 17 as Score
UNION
Select
'02/2012' as DDate, 'Test1' as Cat, 17 as Score
UNION
Select
'03/2012' as DDate, 'Test1' as Cat, 17 as Score
UNION
Select
'04/2012' as DDate, 'Test1' as Cat, 17 as Score
UNION
Select
'05/2012' as DDate, 'Test1' as Cat, 17 as Score
) CompleteTable
LEFT JOIN
(
Select
'01/2012' as DDate, 'Test1' as Cat, 17 as Score
UNION
Select
'02/2012' as DDate, 'Test1' as Cat, 17 as Score
UNION
Select
'03/2012' as DDate, 'Test1' as Cat, 17 as Score
) InCompleteTable
ON CompleteTable.DDate = IncompleteTable.DDate
WHERE IncompleteTable.DDate is null