Can a where clause on a join be used in a view - sql

I'm attempting to create an SQL view that consolidates a number of separate select queries. I've encountered some difficulty putting clauses from the individual select statements into the database view.
A simplified version of my view is:
create or replace view TestView as
select
A.Name,
B.Subscription,
C.Expiry
from
TestTableA as A left outer join TestTableB as B on A.ID = B.A_ID
left outer join TestTableC as C on A.ID = C.A_ID;
I've got two problems with the view:
On the frist join how can I only select record where the Subscription is a specific value AND if it is not that value still retrieve the Name and Expiry columns (in which case the Subscription would be null)?
On the second join how can I specify I only want the record with the most recent expiry date?
Below is my test schema, sample data and desired result set:
create table TestTableA
(
ID int,
Name varchar(32),
Primary Key(ID)
);
create table TestTableB
(
ID int,
A_ID int,
Subscription varchar(32),
Primary Key(ID),
Foreign Key(A_ID) references TestTableA(ID)
);
create table TestTableC
(
ID int,
A_ID int,
Expiry date,
Primary Key(ID),
Foreign Key(A_ID) references TestTableA(ID)
);
create or replace view TestView as
select
A.Name,
B.Subscription,
C.Expiry
from
TestTableA as A left outer join TestTableB as B on A.ID = B.A_ID
left outer join TestTableC as C on A.ID = C.A_ID;
insert into TestTableA values (1, 'Joe');
insert into TestTableB values (1, 1, 'abcd');
insert into TestTableB values (2, 1, 'efgh');
insert into TestTableC values (1, 1, '2012-10-25');
insert into TestTableC values (2, 1, '2012-10-24');
insert into TestTableA values (2, 'Jane');
Desired Results 1:
select * from TestView where Subscription is null or Subscription = 'efgh';
Joe, efgh, 2012-10-25
Jane, ,
Desired Results 2:
select * from TestView where Subscription is null or Subscription = 'xxxx';
Joe, , 2012-10-25
Jane, ,

I'll write query with simple SQL
If you have SQL Server 2005 or higher, you can use outer apply instead of join on subquery with min()
select
A.Name,
B.Subscription,
C.Expiry
from TestTableA as A
left outer join TestTableB as B on A.ID = B.A_ID and B.Subscription in ('abcd', 'efgh')
left outer join
(
select min(T.Expiry) as Expiry, T.A_ID
from TestTableC as T
group by T.A_ID
) as C on A.ID = C.A_ID

create or replace view TestView as
select
A.Name,
B.Subscription,
C.Expiry
from
TestTableA as A left outer join TestTableB as B on A.ID = B.A_ID
left outer join TestTableC as C on A.ID = C.A_ID;
where
B.Subscription is not null
and C.Expiry between (now() - interval 1 minute) and now()

Related

is there a syntactic shortcut similar to coalesce for union?

What I'm trying to do is:
select
A.Fuzz
,A.Fizz
,B.Whiz
from A
left outer join B
on A.Fuzzy B=Wuzzy
To replace:
select
A.Fuzz
,A.Fizz
,B.Whiz
from A
left outer join B
on A.Fuzzy B=Wuzzy
UNION ALL
select
B.wuzz
,A.Fizz
,B.Whiz
from A
left outer join B
on A.Fuzzy B=Wuzzy
May be this is something "near" what you think (done on MSSQL)?. Pls in every question post sample data, expected result, etc.
CREATE TABLE A (ID INT, DESC_A VARCHAR(10));
INSERT INTO A VALUES (1,'A');
INSERT INTO A VALUES (2,'B');
CREATE TABLE B (ID INT, DESC_B VARCHAR(10));
INSERT INTO B VALUES (2,'Z');
INSERT INTO B VALUES (3,'Y');
SELECT COALESCE(A.ID, B.ID) AS ID
, A.DESC_A
, B.DESC_B
FROM A
FULL JOIN B ON A.ID = B.ID
Output:
ID DESC_A DESC_B
1 A NULL
2 B Z
3 NULL Y

Compare two tables, find missing rows and mismatched data

I'd like to compare two tables and get a set of results where the lookup values are mismatched as well as where the key values are missing from the other table. The first part works fine with the following query:
SELECT * FROM (
SELECT mID, mLookup
FROM m) t1
FULL OUTER JOIN (
SELECT aID, aLookup
FROM a) t2
ON t1.mID = t2.aID
WHERE
t1.mID = t2.aID AND
t1.mLookup <> t2.aLookup
However, it doesn't return rows from t1 and t2 where there is no corresponding ID in the other table (because of the ON t1.mID = t2.aID).
How can I achieve both in the same query?
Remove the ID part of the WHERE clause. The FULL OUTER JOIN ON t1.mID = t2.aID is enough to link the tables together. The FULL OUTER JOIN will return both tables in the join even if one does not have a match.
However, the WHERE t1.m_ID = t2.aID clause limits the results to IDs that exist in both tables. This effectively causes the FULL OUTER JOIN to act like an INNER JOIN.
In other words:
SELECT * FROM (
SELECT mID, mLookup
FROM m) t1
FULL OUTER JOIN (
SELECT aID, aLookup
FROM a) t2
ON t1.mID = t2.aID
WHERE
--t1.mID = t2.aID AND -- remove this line
t1.mLookup <> t2.aLookup
-- EDIT --
Re-reading your question, you wanted only the mismatches. In that case, you need to search on where either side's ID is NULL:
SELECT * FROM (
SELECT mID, mLookup
FROM m) t1
FULL OUTER JOIN (
SELECT aID, aLookup
FROM a) t2
ON t1.mID = t2.aID
WHERE
t1.mID IS NULL OR
t2.mID IS NULL OR
t1.mLookup <> t2.aLookup
The where clause of your query filters out those rows that dont have matching "Ids". Try this:
SELECT m.mId, m.mLookup, a.aId, a.aLookup
from m
full outer join a
on a.aId = m.mId
where m.mId is null
or a.aID is null
or m.mLookup <> a.aLookup
The full outer join gets all possible rows, and the where clause keeps all rows where one or the other side are null and, where they match (neither null), keeps only those rows where the "lookup" values differ.
Starting from SQL Server 2008 and also valid for Azure SQL Database, Azure SQL Data Warehouse, Parallel Data Warehouse
Following is the SQL queries;
USE [test]
GO
CREATE TABLE [dbo].[Student1](
[Id] [int] NOT NULL,
[Name] [nvarchar](256) NOT NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Student2](
[Id] [int] NOT NULL,
[Name] [nvarchar](256) NOT NULL
) ON [PRIMARY]
GO
---- You can re-run from here with your data
truncate table [Student1]
truncate table [Student2]
insert into [Student1] values (1, N'سید حیدر')
insert into [Student1] values (2, N'Syed Ali')
insert into [Student1] values (3, N'Misbah Arfin')
insert into [Student2] values (2, N'Syed Ali')
insert into [Student2] values (3, N'Misbah Arfin');
with StudentsAll (Id, [Name]) as
(
select s1.Id, s1.[Name] from Student1 s1
left outer join Student2 s2
on
s1.Id = s2.Id
),
StudentsMatched (Id, [Name]) as
(
select s1.Id, s1.[Name] from Student1 s1
inner join Student2 s2
on
s1.Id = s2.Id
)
select * from StudentsAll
except
select * from StudentsMatched

Parent Child Query without using SubQuery

Let say I have two tables,
Table A
ID Name
-- ----
1 A
2 B
Table B
AID Date
-- ----
1 1/1/2000
1 1/2/2000
2 1/1/2005
2 1/2/2005
Now I need this result without using sub query,
ID Name Date
-- ---- ----
1 A 1/2/2000
2 B 1/2/2005
I know how to do this using sub query but I want to avoid using sub query for some reason?
If I got your meaning right and you need the latest date from TableB, then the query below should do it:
select a.id,a.name,max(b.date)
from TableA a
join TableB b on b.aid = a.id
group by a.id,a.name
SELECT a.ID, a.Name, MAX(B.Date)
FROM TableA A
INNER JOIN TableB B
ON B.ID = A.ID
GROUP BY A.id, A.name
It's a simple aggregation. Looks like you want the highest date per id/name combo.
create table #t1 (id int, Name varchar(10))
create table #t2 (Aid int, Dt date)
insert #t1 values (1, 'A'), (2, 'B')
insert #t2 values (1, '1/1/2000'), (1, '1/2/2000'), (2, '1/1/2005'), (2, '1/2/2005')
;WITH cte (AId, MDt)
as
(
select Aid, MAX(Dt) from #t2 group by AiD
)
select #t1.Id, #t1.Name, cte.MDt
from #t1
join cte
on cte.AId = #t1.Id

Join to only the "latest" record with t-sql

I've got two tables. Table "B" has a one to many relationship with Table "A", which means that there will be many records in table "B" for one record in table "A".
The records in table "B" are mainly differentiated by a date, I need to produce a resultset that includes the record in table "A" joined with only the latest record in table "B". For illustration purpose, here's a sample schema:
Table A
-------
ID
Table B
-------
ID
TableAID
RowDate
I'm having trouble formulating the query to give me the resultset I'm looking for any help would be greatly appreciated.
SELECT *
FROM tableA A
OUTER APPLY (SELECT TOP 1 *
FROM tableB B
WHERE A.ID = B.TableAID
ORDER BY B.RowDate DESC) as B
select a.*, bm.MaxRowDate
from (
select TableAID, max(RowDate) as MaxRowDate
from TableB
group by TableAID
) bm
inner join TableA a on bm.TableAID = a.ID
If you need more columns from TableB, do this:
select a.*, b.* --use explicit columns rather than * here
from (
select TableAID, max(RowDate) as MaxRowDate
from TableB
group by TableAID
) bm
inner join TableB b on bm.TableAID = b.TableAID
and bm.MaxRowDate = b.RowDate
inner join TableA a on bm.TableAID = a.ID
table B join is optional: it depends if there are other columns you want
SELECT
*
FROM
tableA A
JOIN
tableB B ON A.ID = B.TableAID
JOIN
(
SELECT Max(RowDate) AS MaxRowDate, TableAID
FROM tableB
GROUP BY TableAID
) foo ON B.TableAID = foo.TableAID AND B.RowDate= foo.MaxRowDate
With ABDateMap AS (
SELECT Max(RowDate) AS LastDate, TableAID FROM TableB GROUP BY TableAID
),
LatestBRow As (
SELECT MAX(ID) AS ID, TableAID FROM ABDateMap INNER JOIN TableB ON b.TableAID=a.ID AND b.RowDate = LastDate GROUP BY TableAID
)
SELECT columns
FROM TableA a
INNER JOIN LatestBRow m ON m.TableAID=a.ID
INNER JOIN TableB b on b.ID = m.ID
Just for the clarity's sake and to benefit those who will stumble upon this ancient question. The accepted answer would return duplicate rows if there are duplicate RowDate in Table B. A safer and more efficient way would be to utilize ROW_NUMBER():
Select a.*, b.* -- Use explicit column list rather than * here
From [Table A] a
Inner Join ( -- Use Left Join if the records missing from Table B are still required
Select *,
ROW_NUMBER() OVER (PARTITION BY TableAID ORDER BY RowDate DESC) As _RowNum
From [Table B]
) b
On b.TableAID = a.ID
Where b._RowNum = 1
Try using this:
BEGIN
DECLARE #TB1 AS TABLE (ID INT, NAME VARCHAR(30) )
DECLARE #TB2 AS TABLE (ID INT, ID_TB1 INT, PRICE DECIMAL(18,2))
INSERT INTO #TB1 (ID, NAME) VALUES (1, 'PRODUCT X')
INSERT INTO #TB1 (ID, NAME) VALUES (2, 'PRODUCT Y')
INSERT INTO #TB2 (ID, ID_TB1, PRICE) VALUES (1, 1, 3.99)
INSERT INTO #TB2 (ID, ID_TB1, PRICE) VALUES (2, 1, 4.99)
INSERT INTO #TB2 (ID, ID_TB1, PRICE) VALUES (3, 1, 5.99)
INSERT INTO #TB2 (ID, ID_TB1, PRICE) VALUES (1, 2, 0.99)
INSERT INTO #TB2 (ID, ID_TB1, PRICE) VALUES (2, 2, 1.99)
INSERT INTO #TB2 (ID, ID_TB1, PRICE) VALUES (3, 2, 2.99)
SELECT A.ID, A.NAME, B.PRICE
FROM #TB1 A
INNER JOIN #TB2 B ON A.ID = B.ID_TB1 AND B.ID = (SELECT MAX(ID) FROM #TB2 WHERE ID_TB1 = A.ID)
END
This will fetch the latest record with JOIN. I think this will help someone
SELECT cmp.*, lr_entry.lr_no FROM
(SELECT * FROM lr_entry ORDER BY id DESC LIMIT 1)
lr_entry JOIN companies as cmp ON cmp.id = lr_entry.company_id

inner join on null value

I'm not sure if i made a mistake in logic.
If i have a query and i do an inner join with a null value would i always get no results or will it ignore the join and succeed? example
user { id PK, name NVARCHAR NOT NULL, banStatus nullable reference }
if i write and u.banStatus i will receive no rows?
select * from user as u
join banstatus as b on u.banStatus=b.id
where id=1
You don't get the row if the join is null because NULL cannot be equal to anything, even NULL.
If you change it to a LEFT JOIN, then you will get the row.
With an inner join:
select * from user as u
join banstatus as b on u.banStatus=b.id
1, '1', 1, 'Banned'
With a left join:
select * from user as u
left join banstatus as b on u.banStatus=b.id
1, '1', 1, 'Banned'
2, 'NULL', , ''
Using this test data:
CREATE TABLE user (id int, banstatus nvarchar(100));
INSERT INTO user (id, banstatus) VALUES
(1, '1'),
(2, 'NULL');
CREATE TABLE banstatus (id int, text nvarchar(100));
INSERT INTO banstatus (id, text) VALUES
(1, 'Banned');
When you do an INNER JOIN, NULL values do not match with anything. Not even with each other. That is why your query is not returning any rows. (Source)
This is an inner joins on nulls (Oracle syntax):
select *
from user
uu
join banstatus
bb
on uu.banstatus = bb.id
or
uu.banstatus is null and bb.id is null
Nulls are not equal to any other value, so the join condition is not true for nulls. You can achieve the desired result by choosing a different join condition. Instead of
u.banStatus = b.id
use
u.banStatus = b.id OR (u.banStatus IS NULL AND b.id IS NULL)
Some SQL dialects have a more concise syntax for this kind of comparison:
-- PostgreSQL
u.banStatus IS NOT DISTINCT FROM b.id
-- SQLite
u.banStatus IS b.id