Find Unique IDs in SQL from two columns - sql

Query that I have to get teacher and student ids
Select lk.teacherID,lk.studentID from Teacher a inner join student b
on a.classkey = b.classkey
Teacher ID Student ID
123 678
123 876
123 987
456 459
456 789
End Results that I am looking for: -
Find Unique IDs in SQL from two columns
Unique IDs
123
678
876
987
456
459
789

Is this what you want?
select t.teacherId
from teachers t
union
select s.studentId
from students s;
EDIT:
For the clarification, just use cross apply:
Select distinct id
from Teacher t inner join
student s
on t.classkey = s.classkey cross apply
(values (t.teacherId), (s.studentId)) v(id);

Select lk.teacherID as UniqueIDs
From(
Select lk.teacherID,lk.studentID from Teacher a inner join student b
on a.classkey = b.classkey)a
GROUP By teacherID
Union all
Select lk.studentID as UniqueIDs From(
Select lk.teacherID,lk.studentID from Teacher a inner join student b
on a.classkey = b.classkey)a
GROUP By studentID

There no need to take more than a single pass over the data.
IF OBJECT_ID('tempdb..#temp', 'U') IS NOT NULL
DROP TABLE #temp;
CREATE TABLE #temp (
Teacher_ID int NOT NULL,
Student_ID INT NOT NULL
);
INSERT #temp (Teacher_ID, Student_ID) VALUES
(123, 678),
(123, 876),
(123, 987),
(456, 459),
(456, 789);
--======================================
SELECT DISTINCT
UniqueID = x.ID
FROM
#temp t
CROSS APPLY ( VALUES (t.Teacher_ID), (t.Student_ID) ) x (ID);
HTH,
Jason

Related

Changing record values based on whether there are duplicates when two tables are combined

I know I can join Table #1 and Table #2 with a UNION and then filter out duplicate Id's using DISTINCT. However, for the duplicate contacts I'd like to change DrinkPreference to Coke/Pepsi.
Is this possible?
Starting Table #1
Id
FirstName
LastName
DrinkPreference
123
Tom
Bannon
Pepsi
124
Sarah
Smith
Pepsi
Starting Table #2
id
FirstName
LastName
DrinkPreference
125
Jim
Henry
Coke
123
Tom
Bannon
Coke
Table? #3 - combined with DrinkPreference set to Coke/Pepsi where contact exists in both tables?
Id
FirstName
LastName
DrinkPreference
125
Jim
Henry
Coke
123
Tom
Bannon
Coke/Pepsi
124
Sarah
Smith
Pepsi
You can try this one
SELECT coalesce(t1.firstname, t2.firstname) AS firstname,coalesce(t1.lastname,t2.lastname) AS lastname, CASE WHEN t1.drinkpreferences IS NULL THEN t2.drinkpreferences WHEN t2.drinkpreferences IS NULL THEN t1.drinkpreferences
ELSE t1.drinkpreferences || '/' || t2.drinkpreferences END AS drinkpreferences FROM table1 t1 FULL JOIN table2 t2 ON t1.id = t2.id
Achievable using multiple unions and joins.
select distinct FirstName, LastName, case when ct = 2 then 'Coke/Pepsi' else DrinkPreference end
from (
select FirstName, LastName, DrinkPreference, Id from table1
union all
select FirstName, LastName, DrinkPreference, Id from table2) a
left join
(
select count(1)ct, Id from
(select Id from table1
union all
select Id from table2) t1
group by Id
) b on b.Id = a.Id

Line Number and attaching single employee

SQL Server 2000 so no ROW_NUMBER available ....
I need to attach employees to the free lines.
I have a dataset 1 that tells me the free lines per country and region combo.
Table A – available line numbers to use:
Country Region Line Number Employee
---------------------------------------------------
A 1 1 Null
A 1 2 Null
A 2 1 Null
Table B – what employees are available to fill missing line numbers:
Country Region Employee
----------------------------------------
A 1 Dave Smith
A 1 Johnny Cash
A 1 Peter Seller
A 2 David Donald
So required output is
Table C - attaching a single employee to each combo of country, region, line number:
Country Region Line Number Employee
-------------------------------------------------------------
A 1 1 Dave Smith
A 1 2 Johnny Cash
A 2 1 David Donald
I tried a lot of joins, including self joins, and cross joins in SQL Server 2000, but can't get the desired output.
This is my last attempt:
Select
A.Country, A.Region, A.Line Number,
B.Employee
From
Table_A A
Inner Join
Table_B B On A.Country = B.Country and A.Region = B.Region
You need an additional join key after country and region for the assignment. For this, you can use row_number():
select a.*, b.employee
from (select a.*,
row_number() over (partition by country, region order by linenumber) as seqnum
from table_a a
) a join
(select b.*
row_number() over (partition by country, region order by (select null) ) as seqnum
from b
) b
on b.country = a.country and b.region = a.region and b.seqnum = a.seqnum
Just pulling together all of the suggestions, answers, and comments.
--Setting up the tables as given:
CREATE TABLE #e (
Country char(1),
Region int,
LineNumber int,
Employee varchar(50));
INSERT #e
VALUES ('A', 1, 1,NULL)
,('A',1,2,NULL)
,('A',2,1,NULL);
CREATE TABLE #r (
Country char(1),
Region int,
Employee varchar(50));
INSERT #r
VALUES
('A', 1, 'Dave Smith')
,('A', 1, 'Johnny Cash')
,('A', 1, 'Peter Sellers')
,('A', 2, 'David Donald');
--Creating a temporary table with
--a line number to join on.
CREATE TABLE #T(
LineNumber int,
Country char(1),
Region int,
Employee varchar(50));
--Populate the temporary table
--with the line number data.
INSERT INTO #T
(
LineNumber,
Country,
Region,
Employee
)
SELECT
(SELECT
COUNT(*) AS Line
FROM #r AS R2
WHERE R2.Employee <= #r.Employee
AND R2.Region = #r.Region
) AS LineNumber,
Country,
Region,
Employee
FROM #r;
--Set up the final output.
SELECT
A.Country,
A.Region,
A.LineNumber,
B.Employee
FROM
#e A
INNER JOIN
#T B
ON A.Country = B.Country
AND A.Region = B.Region
AND A.LineNumber = B.LineNumber
ORDER BY
A.Country,
A.Region,
A.LineNumber;
--Clean up.
DROP TABLE #r;
DROP TABLE #T;
DROP TABLE #e;
Results:
+---------+--------+------------+--------------+
| Country | Region | LineNumber | Employee |
+---------+--------+------------+--------------+
| A | 1 | 1 | Dave Smith |
| A | 1 | 2 | Johnny Cash |
| A | 2 | 1 | David Donald |
+---------+--------+------------+--------------+

countif type function in SQL where total count could be retrieved in other column

I have 36 columns in a table but one of the columns have data multiple times like below
ID Name Ref
abcd john doe 123
1234 martina 100
123x brittany 123
ab12 joe 101
and i want results like
ID Name Ref cnt
abcd john doe 123 2
1234 martina 100 1
123x brittany 123 2
ab12 joe 101 1
as 123 has appeared twice i want it to show 2 in cnt column and so on
select ID, Name, Ref, (select count(ID) from [table] where Ref = A.Ref)
from [table] A
Edit:
As mentioned in comments below, this approach may not be the most efficient in all cases, but should be sufficient on reasonably small tables.
In my testing:
a table of 5,460 records and 976 distinct 'Ref' values returned in less than 1 second.
a table of 600,831 records and 8,335 distinct 'Ref' values returned in 6 seconds.
a table of 845,218 records and 15,147 distinct 'Ref' values returned in 13 seconds.
You should provide SQL brand to know capabilities:
1) If your DB supports window functions:
Select
*,
count(*) over ( partition by ref ) as cnt
from your_table
2) If not:
Select
T.*, G.cnt
from
( select * from your_table ) T inner join
( select count(*) as cnt from your_table group by ref ) G
on T.ref = G.ref
You can use COUNT with OVERin following:
QUERY
select ID,
Name,
ref,
count(ref) over (partition by ref) cnt
from #t t
SAMPLE DATA
create table #t
(
ID NVARCHAR(400),
Name NVARCHAR(400),
Ref INT
)
insert into #t values
('abcd','john doe', 123),
('1234','martina', 100),
('123x','brittany', 123),
('ab12','joe', 101)

CTE to replace JOIN

Can I use CTE instead of the my code here ? Here are the two tables and my code .
tbl1
D_ID department employee name manager name
112 d01 john d Peter k
113 d01 Emily f kevin s
tbl2
Emp_id employee name D_ID
100 john d 112
200 peter k 112
800 Emily f 113
500 kevin s 113
My code below , what I did is I joined tbl1 with tbl2 on D_ID and employee name and then filter out the records where employee's emp_id > manager's emp_id .
DECLARE #level nvarchar(MAX) =
(
select X.D_ID ,x.employee_NAME, x.emp_ID as employee_id,
y.manager_name ,y.emp_id as manager_id
) + ' '
from (
select distinct b.d_id , a.emp_id as employee_id
from tbl1 a , tbl2 b
where a.d_id=b.d_id and a.employee_NAME=b.employee_NAME
) x ,
(
select distinct b.d_id , a.emp_id as manager_id
from tbl1 a , tbl2 b
where a.d_id=b.d_id and a.employee_NAME=b.manager_NAME
) Y
where x.department=y.department and x.employee_id>=y.manager_id
FOR XML PATH('')
)
IF #level IS NOT NULL
BEGIN
RAISERROR(' employee ID>manager_id: %s',16, 1, #level)
with log;
END;
DESIRED OUTPUT is below as Emily f's employee id is > than her manager's ID .
D_ID employee_NAME employee_id manager_name manager_id
113 Emily f 800 kevin s 500
I think you just need some joins:
select t1.d_id, t1.employee_name, te.emp_id,
t1.employee_name as manager_name, tm.emp_id as manager_id
from tbl1 t1 join
tbl2 te
on t1.employee_name = te.employee_name join
tbl2 tm
on t1.manager_name = tm.employee_name
where te.emp_id > tm.emp_id;
It is quite curious that you are using the names to connect the two tables. Normally, you would use the employee id for this purpose and look up the name using the id.

Cross-multiplying table

I have this SQL code and I want to show the sum of each item on its charge slip and on their receipt:
select item_description, sum(receipt_qty) as Samp1, sum(chargeSlip_qty) as Samp2
from Items inner join Receipt_Detail on (Receipt_Detail.item_number =
Items.item_number)
inner join ChargeSlip_Detail on (ChargeSlip_Detail.item_number =
Items.item_number)
group by item_description
It produces this output:
Acetazolamide 2681 1730
Ascorbic Acid 1512 651
Paracetamol 1370 742
Silk 576 952
But it should be:
Acetazolamide 383 173
Ascorbic Acid 216 93
Paracetamol 274 106
Silk 96 238
What's wrong with my code?
Since you are joining tables, you might have a one-to-many relationship that is causing the problem when you then get the sum(). So you can use subqueries to get the result. This will get the sum() for the receipt and chargeslip for each item_number and then you join that back to your items table to get the final result:
select i.item_description,
r.Samp1,
c.Samp2
from Items i
inner join
(
select sum(receipt_qty) Samp1,
item_number
from Receipt_Detail
group by item_number
) r
on r.item_number = i.item_number
inner join
(
select sum(chargeSlip_qty) Samp2,
item_number
from ChargeSlip_Detail
group by item_number
) c
on c.item_number = i.item_number
Do the GROUP BYs first, per Item_Number, so you don't multiply out rows from Receipt_Detail and ChargeSlip_Detail. That is, you generate the SUM values per Item_Number before JOINing back to Items
select
I.item_description,
R.Samp1,
C.Samp2
from
Items I
inner join
(SELECT item_number, sum(receipt_qty) as Samp1
FROM Receipt_Detail
GROUP BY item_number
) R
on (R.item_number = I.item_number)
inner join
(SELECT item_number, sum(chargeSlip_qty) as Samp2
FROM ChargeSlip_Detail
GROUP BY item_number
) C
on (C.item_number = I.item_number)
A left join returns rows from the left table, and for each row in the left table, all matching rows in the right table.
So for example:
create table Customers (name varchar(50));
insert Customers values
('Tim'),
('John'),
('Spike');
create table Orders (customer_name varchar(50), product varchar(50));
insert Orders values (
('Tim', 'Guitar'),
('John', 'Drums'),
('John', 'Trumpet');
create table Addresses (customer_name varchar(50), address varchar(50));
insert Addresses values (
('Tim', 'Penny Lane 1'),
('John', 'Abbey Road 1'),
('John', 'Abbey Road 2');
Then if you run:
select c.name
, count(o.product) as Products
, count(a.address) as Addresses
from Customers c
left join Orders o on o.customer_name = c.name
left join Addresses a on a.customer_name = c.name
group by name
You get:
name Products Addresses
Tim 1 1
John 4 4
Spike 0 0
But John doesn't have 4 products!
If you run without the group by, you can see why the counts are off:
select *
from Customers c
left join Orders o on o.customer_name = c.name
left join Addresses a on a.customer_name = c.name
You get:
name customer_name product customer_name address
Tim Tim Guitar Tim Penny Lane 1
John John Drums John Abbey Road 1
John John Drums John Abbey Road 2
John John Trumpet John Abbey Road 1
John John Trumpet John Abbey Road 2
Spike NULL NULL NULL NULL
As you can see, the joins end up repeating each other. For each product, the list of addresses is repeated. That gives you the wrong counts. To solve this problem, use one of the excellent other answers:
select c.name
, o.order_count
, a.address_count
from Customers c
left join
(
select customer_name
, count(*) as order_count
from Orders
group by
customer_name
) o
on o.customer_name = c.name
left join
(
select customer_name
, count(*) as address_count
from Addresses
group by
customer_name
) a
on a.customer_name = c.name
The subqueries ensure only one row is joined per customer. The result is much better:
name order_count address_count
Tim 1 1
John 2 2
Spike NULL NULL