Related
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).
Given 3 tables like:
[Table_Main] ----> [Table_Sub] ----> [Table_Prop]
1-N 0-N
I want to select item in [Table_Main] that :
- Have multiple [Table_Sub].
- with [Table_Sub] lines that have both [Table_Prop] and haven't.
To select those value I use :
SELECT Table_Main.Field_ID
FROM Table_Main
INNER JOIN Table_Sub on Table_Main.Field_ID = Table_Sub.Table_Main_Field_ID
LEFT JOIN Table_Prop on Table_Sub.Field_ID = Table_Prop.Table_Sub_Field_ID
If we rename table Family, Child and Pet. I need family where some childs has pet(s) but some child doesn't.
Family: Id, Name
1, Foo -- Family with 2 childs, one of them has a pet
2, Bar -- Family with 2 childs, 0 pet
3, Abc -- Family with 2 childs, both have pet
Child: Id, Family_Id, Name
1, 1, John -- Child of Foo
2, 1, Joe -- Child of Foo
3, 2, Jane
4, 2, Jessica
5, 3, XXX
6, 3, YYY
Pet: Id, Child_Id, Name
1, 2, FooBar -- Joe's pet
2, 5, Huey
3, 6, Dewey
Expected Result:
1, Foo
Family with less than 2 childs is exclude from the exemple has they can satisfy both constraint:
- Has a child with a pet
- Has a child with no pet.
Table Creation :
CREATE TABLE Family(
1 INTEGER NOT NULL PRIMARY KEY
,Foo VARCHAR(20) NOT NULL
);
INSERT INTO Family(1,Foo) VALUES (1,'Foo');
INSERT INTO Family(1,Foo) VALUES (2,'Bar');
INSERT INTO Family(1,Foo) VALUES (3,'Abc');
CREATE TABLE Child(
Id INTEGER NOT NULL PRIMARY KEY
,Family_Id INTEGER NOT NULL
,Name VARCHAR(20) NOT NULL
);
INSERT INTO Child(Id,Family_Id,Name) VALUES (1,1,'John');
INSERT INTO Child(Id,Family_Id,Name) VALUES (2,1,'Joe');
INSERT INTO Child(Id,Family_Id,Name) VALUES (3,2,'Jane');
INSERT INTO Child(Id,Family_Id,Name) VALUES (4,2,'Jessica');
INSERT INTO Child(Id,Family_Id,Name) VALUES (5,3,'XXX');
INSERT INTO Child(Id,Family_Id,Name) VALUES (6,3,'YYY');
CREATE TABLE Pet(
Id INTEGER NOT NULL PRIMARY KEY
,Family_I INTEGER NOT NULL
,Name VARCHAR(20) NOT NULL
);
INSERT INTO Pet(Id,Family_Id,Name) VALUES (1,2,'FooBar');
INSERT INTO Pet(Id,Family_Id,Name) VALUES (2,5,'Huey');
INSERT INTO Pet(Id,Family_Id,Name) VALUES (3,6,'Dewey');
This will give you desired result.
;with family as
(
select 1 FamilyID, 'Foo' Family union select 2, 'Bar' union select 3, 'ABC'
), child as
(
select 1 ChildID, 1 FamilyID ,'John' ChildName union
select 2, 1, 'Joe' union
select 3, 2, 'Jane' union
select 4, 2, 'Jessica' union
select 5, 3, 'XXX'union
select 6, 3, 'YYY'
), pets as
(
select 1 petid , 2 childid, 'FooBar' pet union
select 2, 5, 'Huey' union
select 3, 6, 'Dewey'
)
SELECT T.FamilyID, Max(Family) Family, MIN(CNT) [Min] , MAX(CNT) [Max] FROM
(
SELECT f.FamilyID, C.ChildID, SUM(case when petid is null then 0 else 1 end) CNT FROM Family F
JOIN Child C ON F.FamilyID = C.FamilyID
LEFT JOIN Pets P ON C.ChildID = P.ChildID
GROUP BY F.FamilyID, C.ChildID
) T JOIN Family F on T.FamilyID = F.FamilyID GROUP BY T.FamilyID
HAVING MIN(CNT) = 0 AND MAX(CNT) > 0
Query
select family.ID, family.name
from family
left join child on family.ID = child.family_id
left join pet on pet.child_ID = child.Id
group by family.name,family.ID
having count(child.id) > 1 and count( pet.id) <>0 and count(child.id) > count( pet.id)
Output
looks like you are close but if I understand right:
With parent as (
select 'Charlie' name from dual union all
select 'Ben' name from dual union all
select 'Bob' name from dual union all
select 'Harry' name from dual
)
,child as (
select 'Ben' parentname, 'Bebbie' name from dual union all
select 'Ben' parentname, 'Tilda' name from dual union all
select 'Bob' parentname, 'Shara' name from dual union all
select 'Bob' parentname, 'Sandra' name from dual
)
,pet as (
select 'Tilda' childname, 'Dog' pet from dual union all
select 'Tilda' childname, 'Cat' pet from dual union all
select 'Shara' childname, 'Bird' pet from dual union all
select 'Shara' childname, 'Snake' pet from dual
)
select pa.name,ch.name,count(pe.pet)
from parent pa
inner join child ch on ch.parentname = pa.name
left join pet pe on pe.childname = ch.name
group by pa.name,ch.name
I am using Oracle (SQL Developer). Please find below the example and an outcome which would like to get (purpose of select is to find out people who submitted project A and have not done any activities in project B yet):
Data table:
CREATE TABLE "XXX"."TABLE1"
( "STATUS" VARCHAR2(20 BYTE),
"PROJECT_NAME" VARCHAR2(20 BYTE),
"VERSION_NUMBER" NUMBER,
"PERSON" VARCHAR2(20 BYTE)
);
Insert into XXX.TABLE1 (STATUS,PROJECT_NAME,VERSION_NUMBER,PERSON) values ('SUBMITTED','A','0','PETER');
Insert into XXX.TABLE1 (STATUS,PROJECT_NAME,VERSION_NUMBER,PERSON) values ('SUBMITTED','A','0','JOHN');
Insert into XXX.TABLE1 (STATUS,PROJECT_NAME,VERSION_NUMBER,PERSON) values ('SUBMITTED','A','1','JOHN');
Insert into XXX.TABLE1 (STATUS,PROJECT_NAME,VERSION_NUMBER,PERSON) values ('NEW','A','2','JOHN');
Insert into XXX.TABLE1 (STATUS,PROJECT_NAME,VERSION_NUMBER,PERSON) values ('SUBMITTED','A','0','MARY');
Insert into XXX.TABLE1 (STATUS,PROJECT_NAME,VERSION_NUMBER,PERSON) values ('SUBMITTED','B','0','PETER');
Insert into XXX.TABLE1 (STATUS,PROJECT_NAME,VERSION_NUMBER,PERSON) values ('NEW','B','1','PETER');
Insert into XXX.TABLE1 (STATUS,PROJECT_NAME,VERSION_NUMBER,PERSON) values ('SUBMITTED','B','0','JOHN');
Created table should look like this:
TABLE1:
TABLE1.STATUS TABLE1.PROJECT_NAME TABLE1.VERSION_NUMBER TABLE1.PERSON
SUBMITTED A 0 PETER
SUBMITTED A 0 JOHN
SUBMITTED A 1 JOHN
NEW A 2 JOHN
SUBMITTED A 0 MARY
SUBMITTED B 0 PETER
NEW B 1 PETER
SUBMITTED B 0 JOHN
Result what I want get is this:
STATUS PROJECT_NAME VERSION_NUMBER PERSON STATUS_1 PROJECT_NAME_1 VERSION_NUMBER_1 PERSON_1
SUBMITTED A 0 PETER NEW B 1 PETER
SUBMITTED A 1 JOHN SUBMITTED B 0 JOHN
SUBMITTED A 0 MARY
Select which I am using now is:
select t.*,v.*
from TABLE1 t
left outer join ( select u.*
from TABLE1 u
where exists (select max(z.VERSION_NUMBER)
,z.PERSON
,z.PROJECT_NAME
from TABLE1 z
where z.PROJECT_NAME = 'B'
and u.PROJECT_NAME = z.PROJECT_NAME
and u.PERSON = z.PERSON
group by z.PERSON, z.PROJECT_NAME
having u.VERSION_NUMBER = max(z.VERSION_NUMBER))) v
on t.PERSON = v.PERSON
where exists (select max (w.VERSION_NUMBER)
,w.PERSON
,w.PROJECT_NAME
from TABLE1 w
where w.PROJECT_NAME = 'A'
and w.STATUS = 'SUBMITTED'
and t.PROJECT_NAME = w.PROJECT_NAME
and t.PERSON = w.PERSON
group by w.PERSON, w.PROJECT_NAME
having t.VERSION_NUMBER = max (w.VERSION_NUMBER))
QUESTION: What would be best(right) way to write such select (best practice), should I better use Analytic functions or use something else instead of EXISTS?
I think you've over-complicated this...
WITH
project_status (status, project_name, version_number, person)
AS
(SELECT 'SUBMITTED','A','0','PETER' FROM dual UNION ALL
SELECT 'SUBMITTED','A','0','JOHN' FROM dual UNION ALL
SELECT 'SUBMITTED','A','1','JOHN' FROM dual UNION ALL
SELECT 'NEW','A','2','JOHN' FROM dual UNION ALL
SELECT 'SUBMITTED','A','0','MARY' FROM dual UNION ALL
SELECT 'SUBMITTED','B','0','PETER' FROM dual UNION ALL
SELECT 'NEW','B','1','PETER' FROM dual UNION ALL
SELECT 'SUBMITTED','B','0','JOHN' FROM dual
)
SELECT DISTINCT
ps.person
,ps.project_name
,ps.status
FROM
project_status ps
WHERE 1=1
AND ps.project_name = 'A'
AND ps.status = 'SUBMITTED'
AND NOT EXISTS
(SELECT 1
FROM project_status ps2
WHERE ps2.person = ps.person
AND ps2.project_name = 'B'
)
;
purpose of select is to find out people who submitted project A and
have not done any activities in project B yet
If your purpose is just to get the people, then you don't need the complete rows. One method to answer this is to use group by and having:
select t1.person
from "XXX"."TABLE1" t1
group by t1.person
having sum(case when project_name = 'A' and status = 'New' then 1 else 0 end) > 0 and
sum(case when project_name = 'B' then 1 else 0 end) = 0;
If you need the complete rows, then Christian has a reasonable solution.
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
I have a result set as below:
Item ExpectedQty ReceivedQty Short
Item01 30 45 5
Item01 20 45 5
Item02 40 38 2
item03 50 90 10
item03 30 90 10
item03 20 90 10
query is:
select a.Item, a.ExpectedQty,b.ReceivedQty, b.Short
from a join b on a.Item = b.Item
I need to get result as in second chart. Basically I have a total of received quantity in each line and I need to show received quantity against Expected Quantity, if there is any shortage I need to show in last line.
Expected:
Item ExpectedQty ReceivedQty Short
item01 30 30 0
item01 20 15 5
item02 40 38 2
item03 50 50 0
item03 30 30 0
item03 20 10 10
Thanks in advance.
Edited,
Vession 02 ;
-- Just a brief of business scenario is table has been created for a good receipt.
-- So here we have good expected line with PurchaseOrder(PO) in first few line.
-- And then we receive each expected line physically and that time these
-- quantity may be different
-- due to business case like quantity may damage and short quantity like that.
-- So we maintain a status for that eg: OK, Damage, also we have to calculate
-- short quantity
-- based on total of expected quantity of each item and total of received line.
if object_id('DEV..Temp','U') is not null
drop table Temp
CREATE TABLE Temp
(
ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
Item VARCHAR(32),
PO VARCHAR(32) NULL,
ExpectedQty INT NULL,
ReceivedQty INT NULL,
[STATUS] VARCHAR(32) NULL,
BoxName VARCHAR(32) NULL
)
-- Please see first few line with PO data will be the expected lines,
-- and then rest line will be received line
INSERT INTO TEMP (Item,PO,ExpectedQty,ReceivedQty,[STATUS],BoxName)
SELECT 'ITEM01','PO-01','30',NULL,NULL,NULL UNION ALL
SELECT 'ITEM01','PO-02','20',NULL,NULL,NULL UNION ALL
SELECT 'ITEM02','PO-01','40',NULL,NULL,NULL UNION ALL
SELECT 'ITEM03','PO-01','50',NULL,NULL,NULL UNION ALL
SELECT 'ITEM03','PO-02','30',NULL,NULL,NULL UNION ALL
SELECT 'ITEM03','PO-03','20',NULL,NULL,NULL UNION ALL
SELECT 'ITEM04','PO-01','30',NULL,NULL,NULL UNION ALL
SELECT 'ITEM01',NULL,NULL,'20','OK','box01' UNION ALL
SELECT 'ITEM01',NULL,NULL,'25','OK','box02' UNION ALL
SELECT 'ITEM01',NULL,NULL,'5','DAMAGE','box03' UNION ALL
SELECT 'ITEM02',NULL,NULL,'38','OK','box04' UNION ALL
SELECT 'ITEM02',NULL,NULL,'2','DAMAGE','box05' UNION ALL
SELECT 'ITEM03',NULL,NULL,'30','OK','box06' UNION ALL
SELECT 'ITEM03',NULL,NULL,'30','OK','box07' UNION ALL
SELECT 'ITEM03',NULL,NULL,'30','OK','box08' UNION ALL
SELECT 'ITEM03',NULL,NULL,'10','DAMAGE','box09' UNION ALL
SELECT 'ITEM04',NULL,NULL,'25','OK','box10'
-- Below Table is my expected result based on above data.
-- I need to show those data following way.
-- So I appreciate if you can give me an appropriate query for it.
-- Note: first row is blank and it is actually my table header. :)
SELECT ''as'ITEM', ''as'PO#', ''as'ExpectedQty',''as'ReceivedQty',
''as'DamageQty' ,''as'ShortQty' UNION ALL
SELECT 'ITEM01','PO-01','30','30','0' ,'0' UNION ALL
SELECT 'ITEM01','PO-02','20','15','5' ,'0' UNION ALL
SELECT 'ITEM02','PO-01','40','38','2' ,'0' UNION ALL
SELECT 'ITEM03','PO-01','50','50','0' ,'0' UNION ALL
SELECT 'ITEM03','PO-02','30','30','0' ,'0' UNION ALL
SELECT 'ITEM03','PO-03','20','10','10','0' UNION ALL
SELECT 'ITEM04','PO-01','30','25','0' ,'5'
One part of the problem is to get the running totals of expected item qunatities. For that you'd need a way to distinguish rows with same items from each other and a rule for the order of discharging same item quantities.
For the purpose of my attempt at solving your problem I'm going to assume there's a timestamp column whose values provide the order of discharge and are unique within same item groups.
Here's the sample data definition I was testing my solution on:
CREATE TABLE TableA (Item varchar(50), ExpectedQty int, Timestamp int);
INSERT INTO TableA
SELECT 'Item01', 30, 1 UNION ALL
SELECT 'Item01', 20, 2 UNION ALL
SELECT 'Item02', 40, 1 UNION ALL
SELECT 'item03', 50, 1 UNION ALL
SELECT 'item03', 30, 2 UNION ALL
SELECT 'item03', 20, 3;
CREATE TABLE TableB (Item varchar(50), ReceivedQty int);
INSERT INTO TableB
SELECT 'Item01', 45 UNION ALL
SELECT 'Item02', 38 UNION ALL
SELECT 'item03', 90;
And here's my solution:
SELECT
Item,
ExpectedQty,
ReceivedQty = CASE
WHEN RemainderQty >= 0 THEN ExpectedQty
WHEN RemainderQty < -ExpectedQty THEN 0
ELSE RemainderQty + ExpectedQty
END,
Short = CASE
WHEN RemainderQty >= 0 THEN 0
WHEN RemainderQty < -ExpectedQty THEN ExpectedQty
ELSE ABS(RemainderQty)
END
FROM (
SELECT
a.Item,
a.ExpectedQty,
RemainderQty = b.ReceivedQty - a.RunningTotalQty
FROM (
SELECT
a.Item,
a.Timestamp,
a.ExpectedQty,
RunningTotalQty = SUM(a2.ExpectedQty)
FROM TableA a
INNER JOIN TableA a AS a2 ON a.Item = a2.Item AND a.Timestamp >= a2.Timestamp
GROUP BY
a.Item,
a.Timestamp,
a.ExpectedQty
) a
INNER JOIN TableB b ON a.Item = b.Item
) s
select a.Item, a.ExpectedQty,b.ReceivedQty, (a.ExpectedQty - b.ReceivedQty) as 'Short' from a join b on a.Item = b.Item
SELECT a.ExpectedQty,
b.ReceivedQty,
CASE WHEN b.ReceivedQty < a.ExpectedQty
THEN b.ReceivedQty - a.ExpectedQty
ELSE 0
END Short
FROM dbo.a a
INNER JOIN dbo.b b
ON a.ItemId = b.ItemId