Picking info using junction table(SQL SERVER 2005) [ SET BASED] - sql

I have 3 tables
1) tblPurchaser having 2 columns:
PurchaserId PurchaserName
1 A1
2 A2
3 A3
2) tblCar having 2 columns:
CarId Carname
11 C1
12 C2
13 C3
14 C4
And the last is a junction table tblInformation where the information about those persons are given who has purchased cars.
PurchaserId CarId
1 11
1 12
2 11
2 13
Now I need to write a set based query where I can be able to obtain the information of those cars which has not been purchased by the persons
Desired Output
PurchaserId CarId
1 13
1 14
2 12
2 14
3 11
3 12
3 13
3 14
Note: This is a real time problem which I am implementing in my project. Because of privacy of company, I have changed the tables and information. But my situation is something similar
Please help me
Edited
So far I have written this query:
SELECT 1 as purchaserid,carid from tblcar
where carid not in (select carid from tblinformation where purchaserid = 1)
union all
SELECT 2 as purchaserid,carid from tblcar
where carid not in (select carid from tblinformation where purchaserid = 2)
union all
SELECT 3 as purchaserid,carid from tblcar
where carid not in (select carid from tblinformation where purchaserid = 3)
But as you can make out that i am hardcoding the purchaserid's. And also in real time I will not know how many id's will be there. So everything has to be done at runtime.
Please helpenter code here

Clue: NOT EXISTS
You should really try to do some homework yourself... 3rd question today...

LEFT JOIN ... WHERE ... IS NULL to the rescue:
SELECT tblPurchaser.PurchaserId, tblCar.CarId
FROM tblPurchaser JOIN tblCar
LEFT JOIN tblInformation ON(
tblPurchaser.PurchaserId = tblInformation.PurchaserId
AND tblCar.CarId = tblInformation.CarId)
WHERE tblInformation.CarId IS NULL

Try this
select pur.PurchaserId, car.CarId
from tblPurchaser pur, tblCar car
where not exists (select 1 from tblInformation where PurchaserId = pur. PurchaserId and CarId = car. CarId)
order by pur.PurchaserId;

Try this:
SELECT PurchaserID, CarID
FROM Purchasers
CROSS JOIN Cars
EXCEPT
SELECT *
FROM tblInformation
Here is a SQL script that demonstrates that this technique works correctly:
declare #soPurchaser table(PurchaserId int, PurchaserName varchar(4));
insert #soPurchaser select 1,'A1'
insert #soPurchaser select 2,'A2'
insert #soPurchaser select 3,'A3'
Declare #SOtblCar table(CarId int, Carname varchar(4))
insert #SOtblCar select 11,'C1'
insert #SOtblCar select 12,'C2'
insert #SOtblCar select 13,'C3'
insert #SOtblCar select 14,'C4'
Declare #SOtblInfo table(PurchaserId int, CarId int)
insert #SOtblInfo select 1,11
insert #SOtblInfo select 1,12
insert #SOtblInfo select 2,11
insert #SOtblInfo select 2,13
SELECT PurchaserID, CarID
FROM #soPurchaser
CROSS JOIN #SOtblCar
EXCEPT
SELECT *
FROM #SOtblInfo
The SQL Set operators (UNION, INTERSECT, and EXCEPT) all operate on two table-sets. You will note that they have no way to map the columns from one set to the other. In all cases in SQL when column must be mapped to each other, but there is no syntax to do it explicitly, then they are always mapped based on column order.
So in this one case, if you have one of the table's column order wrong, then it will not work correctly.

Related

SQL: create a view comparing different rows

I have data for movies that looks something like this:
cast_id | cast_name | movie_id
1 A 11
2 B 11
3 C 11
4 D 12
5 E 12
1 A 13
I want to create a view where I compare two different cast members so that I will start with something like this:
CREATE VIEW compare(cast_id_1, cast_id_2, num_movies);
SELECT * FROM compare LIMIT 1;
(1,2,2)
where I am looking at actor A and actor B, who have a total of 2 movies between the two of them.
Not sure how to compare the two different rows and my searchers so far have been unsuccessful. Any help is much appreciated!
That's a self-join:
create view myview as
select t1.cast_id cast_id_1, t2.cast_id cast_id_2, count(*) num_movies
from mytable t1
inner join mytable t2 on t2.movie_id = t1.movie_id and t1.cast_id < t2.cast_id
group by t1.cast_id, t2.cast_id
Thives generates all combinations of cast members that once appeared in the same movie, with the total number of movies. Join condition t1.cast_id < t2.cast_id is there to avoid "mirror" records.
You can then query the view. If you want members that have two common movies (which is actually not showing in your sample data...):
select * from myview where num_movies = 2
I'm thinking a procedure might be helpful. This stored procedure takes the 2 cast_id's and num_movies as input parameters. It selects the movie_id's of the movies the two cast_id's have appeared in together. Then based on whether or not that number exceeds the num_movies parameter: either 1) a list of movies (release dates, director, etc.) is returned, else the message 'Were not in 2 movies together' is returned.
drop proc if exists TwoMovieActors;
go
create proc TwoMovieActors
#cast_id_1 int,
#cast_id_2 int,
#num_movies int
as
set nocount on;
declare #m table(movie_id int unique not null,
rn int not null);
declare #rows int;
with
cast_cte as (
select *, row_number() over (partition by movie_id order by cast_name) rn
from movie_casts mc
where cast_id in(#cast_id_1, #cast_id_2))
insert #m
select movie_id, row_number() over (order by movie_id) rn
from cast_cte
where rn=2
select #rows=##rowcount;
if #rows<#num_movies
select concat('Were not in ', cast(#num_movies as varchar(11)), ' movies together');
else
select m.movie_id, mv.movie_name, mv.release_date, mv.director
from #m m
join movies mv on m.movie_id=mv.movie_id;
To execute it would be something like
exec TwoMovieActors 1, 2, 2;

Specific format Join results on SQL Server

I have trawled the internet looking for a solution but nothing so far.
Here are 2 sample tables joined on SID/ID
SID Name Attendance Class
1 abc good 1A
2 xyz bad 1B
3 dsk good 1A
4 uij bad 1B
5 sss bad 1A
6 fff good 1D
7 ccc good 1A
ID Lesson Result
1 Read Pass-67%
1 Write Pass-89%
1 Sing Pass-99%
2 Read Pass-75%
3 Sing Fail-47%
3 Read Pass-55%
4 Write Pass-90%
4 Sing Fail-10%
The results need to be in the following format.
A row showing the student name, followed by rows of the students' results.
If a student does not have any results they will not be included.
1, abc, good, 1A
1, Read, Pass-67%
1, Write, Pass-89%
1, Sing, Pass-99%
2, xyz, bad, 1B
2, Read, Pass-75%
3, dsk, good, 1A
3, Sing, Fail-47%
3, Read, Pass-55%
4, uij, bad, 1B
4, Write, Pass-90%
4, Sing, Fail-10%
I attempted using Union to no avail, it is similar to a pivot have not had any luck with that either. Is assume i’m missing a trick here, how can I get this done?
I have included the data if it makes it any easier!
CREATE TABLE RESULTS (ID Int, Lesson varchar(12), Result nvarchar(8))
insert into RESULTS (ID, Lesson, Result)
values
(1,'Read', 'Pass-67%'),
(1,'Write', 'Pass-89%'),
(1,'Sing', 'Pass-99%'),
(2,'Read', 'Pass-75%'),
(3,'Sing', 'Fail-47%'),
(3,'Read','Pass-55%'),
(4,'Write', 'Pass-90%'),
(4,'Sing', 'Fail-10%')
CREATE TABLE STUDENTS (ID int, Name varchar(5), Attendance nvarchar(10),
Class nvarchar (3))
insert into STUDENTS values
(1,'abc','good','1A'),
(2,'xyz','bad','1B'),
(3,'dsk','good','1A'),
(4,'uij','bad','1B'),
(5,'sss','bad','1A'),
(6,'fff','good','1D'),
(7,'ccc','good','1A')
You can use a UNION with a few workarounds.
;WITH Data AS
(
SELECT
S.ID,
S.Name,
S.Attendance,
S.Class,
IsStudent = 1
FROM
Students AS S
WHERE
EXISTS (SELECT 'at least one result' FROM Results AS R WHERE R.ID = S.ID)
UNION ALL
SELECT
ID = R.ID,
Name = R.Lesson,
Attendance = R.Result,
Class = NULL,
IsStudent = 0
FROM
Results AS R
)
SELECT
D.ID,
D.Name,
D.Attendance,
D.Class
FROM
Data AS D
ORDER BY
ID,
IsStudent DESC
But, as you can see on the final column names, you are mixing different data together which is not a good thing to do.
Use union all :
select t.*
from(select ID, Name, Attendance, class
from STUDENTS s
where exists (select 1 from RESULTS where id = s.id) union all
select ID, Lesson, Result, null
from RESULTS r
) t
order by id, (case when class is not null then 0 else 1 end);
Simply concat those columns and Union
SELECT CONVERT(VARCHAR(10),id)+' , '+Name+' , '+Attendance
AS ResultSet INTO #T FROM dbo.STUDENTS
UNION ALL
SELECT CONVERT(VARCHAR(10),ID)+' , '+Lesson+' , '+ Result
FROM dbo.RESULTS
SELECT * FROM #T ORDER BY ResultSet
DROP TABLE #T

Linking together different columns in a SQL table?

I'm not very experienced with advance SQL queries, I'm familiar with basic statements and basic joins, currently trying to figure out how to write a query that seems to be out of my depth and I haven't been able to find a solution from google so far and I'm hoping somebody might be able to point me in the right direction.
The table I'm working with has an ID column, and a 'parent id' column.
I'm looking for all descendants of ID '1' - rows with a parent ID of '1', rows with a parent ID equal to any row's ID with a parent ID of '1' etc. Currently I've been doing this manually but there are hundreds of descendants so far and I feel like there's a way to put this into one query.
Any help would be appreciated, if this is unclear I can also try to clarify.
EDIT - I got it working with the following query:
with cteMappings as (
select map_id, parent_map_id, map_name
from admin_map
where map_id = '1'
union all
select a.map_id, a.parent_map_id, a.map_name
from admin_map a
inner join cteMappings m
on a.parent_map_id = m.map_id
)
select map_id, parent_map_id, map_name
from cteMappings
Sounds like it can be achieved by Common Table Expression:
DECLARE #temp TABLE (id INT IDENTITY(1, 1), parent_id INT);
INSERT #temp
SELECT NULL
UNION ALL
SELECT 1
UNION ALL
SELECT 2
UNION ALL
SELECT NULL
SELECT * FROM #temp
; WITH HierarchyTemp (id, parent_id, depth) AS (
SELECT id, parent_id, 0
FROM #temp
WHERE id = 1
UNION ALL
SELECT t.id, t.parent_id, ht.depth + 1
FROM #temp t
INNER JOIN HierarchyTemp ht ON ht.id = t.parent_id
)
SELECT *
FROM HierarchyTemp
So the above example is creating a table variable with 4 rows:
id parent_id
1 NULL
2 1
3 2
4 NULL
Result of descendent of id '1' (also including itself, but can be excluded with an additional WHERE clause):
id parent_id depth
1 NULL 0
2 1 1
3 2 2

Need query to select direct and indirect customerID aliases

I need a query that will return all related alias id's from either column. Shown here are some alias customer ids, among thousands of other rows. If the input parameter to a query is id=7, I need a query that would return 5 rows (1,5,7,10,22). That is because they are all aliases of one-another. For example, 22 and 10 are indirect aliases of 7.
CustomerAlias
--------------------------
AliasCuID AliasCuID2
--------------------------
1 5
1 7
5 7
10 5
22 1
Here is an excerpt from the customer table.
Customer
----------------------------------
CuID CuFirstName CuLastName
----------------------------------
1 Mike Jones
2 Fred Smith
3 Jack Jackson
4 Emily Simpson
5 Mike Jones
6 Beth Smith
7 Mike jones
8 Jason Robard
9 Emilie Jiklonmie
10 Michael jones
11 Mark Lansby
12 Scotty Slash
13 Emilie Jiklonmy
22 mike jones
I've been able to come close, but I cannot seem to select the indirectly related aliases correctly. Given this query:
SELECT DISTINCT Customer.CuID, Customer.CuFirstName, Customer.CuLastName
FROM Customer WHERE
(Customer.CuID = 7) OR (Customer.CuID IN
(SELECT AliasCuID2
FROM CustomerAlias AS CustomerAlias_2
WHERE (AliasCuID = 7))) OR (Customer.CuID IN
(SELECT AliasCuID
FROM CustomerAlias AS CustomerAlias_1
WHERE (AliasCuID2 = 7)))
Returns 3 out of 5 of the desired ids of course. This lacks the indirectly related aliased id of 10 and 22 in the result rows.
1 Mike Jones
5 Mike Jones
7 Mike jones
* Based on suggestions below, I am trying a CTE hierarchical query.
I have this now after following some suggestions. It works for some, as long as the records in the table reference enough immediate ids. But, if the query uses id=10, then it still comes up short, just by the nature of the data.
DECLARE #id INT
SET #id = 10;
DECLARE #tmp TABLE ( a1 INT, a2 INT, Lev INT );
WITH Results (AliasCuID, AliasCuID2, [Level]) AS (
SELECT AliasCuID,
AliasCuID2,
0 as [Level]
FROM CustomerAlias
WHERE AliasCuID = #id OR AliasCuID2 = #id
UNION ALL
-- Recursive step
SELECT a.AliasCuID,
a.AliasCuID2,
r.[Level] + 1 AS [Level]
FROM CustomerAlias a
INNER JOIN Results r ON a.AliasCuID = r.AliasCuID2 )
INSERT INTO #tmp
SELECT * FROM Results;
WITH Results3 (AliasCuID, AliasCuID2, [Level]) AS (
SELECT AliasCuID,
AliasCuID2,
0 as [Level]
FROM CustomerAlias
WHERE AliasCuID = #id OR AliasCuID2 = #id
UNION ALL
-- Recursive step
SELECT a.AliasCuID,
a.AliasCuID2,
r.[Level] + 1 AS [Level]
FROM CustomerAlias a
INNER JOIN Results3 r ON a.AliasCuID2 = r.AliasCuID )
INSERT INTO #tmp
SELECT * FROM Results3;
SELECT DISTINCT a1 AS id FROM #tmp
UNION ALL
SELECT DISTINCT a2 AS id FROM #tmp
ORDER BY id
Note that this is a simplified the query to just give a list of related ids.
---
id
---
5
5
7
10
But, it is still unable to pull in ids 1 and 22.
This is not an easy problem to solve unless you have some idea of the depth of your search (https://stackoverflow.com/a/7569520/1803682) - which it looks like you do not - and take a brute force approach to it.
Assuming you do not know the depth you will need to write a stored proc. I followed this approach for a nearly identical problem: https://dba.stackexchange.com/questions/7147/find-highest-level-of-a-hierarchical-field-with-vs-without-ctes/7161#7161
UPDATE
If you don't care about the chain of how the alias's were created - I would run a script recursively to make them all refer to a single (master?) record. Then you can easily do the search and it will be quick - not a solution if you care about how the alias's get traversed though.
I created a SQL Fiddle for SQL Server 2012. Please let me know if you can or cannot access it.
My thought here was that you'd want to just keep checking the left and right branches recursively, separately. This logic probably falls apart if the relationships bounce between left and right. You could set up a third CTE to reference the first two, but joining on left to right and right to left, but ain't nobody got time for that.
The code is below as well.
CREATE TABLE CustomerAlias
(
AliasCuID INT,
AliasCuID2 INT
)
GO
INSERT INTO CustomerAlias
SELECT 1,5
UNION SELECT 1, 7
UNION SELECT 5, 7
UNION SELECT 10, 5
UNION SELECT 22, 1
GO
DECLARE #Value INT
SET #Value = 7
; WITH LeftAlias AS
(
SELECT AliasCuID
, AliasCuID2
FROM CustomerAlias
WHERE AliasCuID2 = #Value
UNION ALL
SELECT a.AliasCuID
, a.AliasCuID2
FROM CustomerAlias a
JOIN LeftAlias b
ON a.AliasCuID = b.AliasCuID2
)
, RightAlias AS
(
SELECT AliasCuID
, AliasCuID2
FROM CustomerAlias
WHERE AliasCuID = #Value
UNION ALL
SELECT a.AliasCuID
, a.AliasCuID2
FROM CustomerAlias a
JOIN LeftAlias b
ON a.AliasCuID2 = b.AliasCuID
)
SELECT DISTINCT A
FROM
(
SELECT A = AliasCuID
FROM LeftAlias
UNION ALL
SELECT A = AliasCuID2
FROM LeftAlias
UNION ALL
SELECT A = AliasCuID
FROM RightAlias
UNION ALL
SELECT A = AliasCuID2
FROM RightAlias
) s
ORDER BY A

SQL if statement with two tables

Given the following tables:
table objects
id Name rating
1 Megan 9
2 Irina 10
3 Vanessa 7
4 Samantha 9
5 Roxanne 1
6 Sonia 8
swap table
id swap_proposalid counterpartyid
1 4 2
2 3 2
Everyone wants the ten. I would like to make a list for Irina of possible swaps where id 4 and 3 don't appear because the propositions are already there.
output1
id Name rating
1 Megan 9
5 Roxanne 1
6 Sonia 8
Thanks
This should do the trick:
SELECT o.id, o.Name, o.rating
FROM objects o
LEFT JOIN swap s on o.id = s.swap_proposalid
WHERE s.id IS NULL
AND o.Name != 'Irina'
This works
SELECT mt2.ID, mt2.Name, mt2.Rating
FROM [MyTable] mt2 -- Other Candidates
, [MyTable] mt1 -- Candidate / Subject (Irina)
WHERE mt2.ID NOT IN
(
SELECT st.swap_proposalid
FROM SwapTable st
WHERE
st.counterpartyid = mt1.ID
)
AND mt1.ID <> mt2.ID -- Don't match Irina with Irina
AND mt1.Name = 'Irina' -- Find other swaps for Irina
-- Test Data
CREATE TABLE MyTable
(
ID INT,
Name VARCHAR(100),
Rating INT
)
GO
CREATE TABLE SwapTable
(
ID INT,
swap_proposalid INT,
counterpartyid INT
)
GO
INSERT INTO MyTable VALUES(1 ,'Megan', 9)
INSERT INTO MyTable VALUES(2 ,'Irina', 10)
INSERT INTO MyTable VALUES(3 ,'Vanessa', 7)
INSERT INTO MyTable VALUES(4 ,'Samantha', 9)
INSERT INTO MyTable VALUES(5 ,'Roxanne', 1)
INSERT INTO MyTable VALUES(6 ,'Sonia', 8)
INSERT INTO SwapTable(ID, swap_proposalid, counterpartyid)
VALUES (1, 4, 2)
INSERT INTO SwapTable(ID, swap_proposalid, counterpartyid)
VALUES (1, 3, 2)
Guessing that the logic involves identifying the objects EXCEPT the highest rated object EXCEPT propositions with the highest rated object e.g. (using sample DDL and data kindly posted by #nonnb):
WITH ObjectHighestRated
AS
(
SELECT ID
FROM MyTable
WHERE Rating = (
SELECT MAX(T.Rating)
FROM MyTable T
)
),
PropositionsForHighestRated
AS
(
SELECT swap_proposalid AS ID
FROM SwapTable
WHERE counterpartyid IN (SELECT ID FROM ObjectHighestRated)
),
CandidateSwappersForHighestRated
AS
(
SELECT ID
FROM MyTable
EXCEPT
SELECT ID
FROM ObjectHighestRated
EXCEPT
SELECT ID
FROM PropositionsForHighestRated
)
SELECT *
FROM MyTable
WHERE ID IN (SELECT ID FROM CandidateSwappersForHighestRated);