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 |
+---------+--------+------------+--------------+
Related
I am trying to write a query to get the following results
Expecting the result are in the following table
HouseName Address CurrentResident
------------------------------------------
A 1 Market St Smith
B 2 Market St Einstein
X 3 Market St [Vacant]
D 4 Market St [Vacant]
Please help
The SQL query for the tables
CREATE TABLE House
(
HouseName Varchar(1),
Address Varchar(255)
);
CREATE TABLE Events
(
Id Integer,
HouseName Varchar(1),
EventName Varchar(255),
Date Date,
Family Varchar(255)
);
INSERT House VALUES('A', '1 Market St');
INSERT House VALUES('B', '2 Market St');
INSERT House VALUES('X', '3 Market St');
INSERT House VALUES('D', '4 Market St');
INSERT Events VALUES(1,'A','MoveIn','2005-01-31','Smith');
INSERT Events VALUES(2,'A','Remodel','2005-03-31','Smith');
INSERT Events VALUES(3,'A','Remodel','2008-03-31','Smith');
INSERT Events VALUES(4,'A','CleanUp','2008-05-31','Smith');
INSERT Events VALUES(5,'B','MoveIn','2005-01-31','Newton');
INSERT Events VALUES(6,'B','MoveOut','2008-05-01','Newton');
INSERT Events VALUES(7,'B','MoveIn','2008-12-01','Einstein');
INSERT Events VALUES(8,'D','MoveIn','2007-08-31','Soo');
INSERT Events VALUES(9,'D','MoveOut','2010-08-13','Soo');
The two resulting tables are as follows
HouseName Address
A 1 Market St
B 2 Market St
X 3 Market St
D 4 Market St
Events
Id HouseName EventName Date Family
1 A MoveIn 2005-01-31 Smith
2 A Remodel 2005-03-31 Smith
3 A Remodel 2008-03-31 Smith
4 A CleanUp 2008-05-31 Smith
5 B MoveIn 2005-01-31 Newton
6 B MoveOut 2008-05-01 Newton
7 B MoveIn 2008-12-01 Einstein
8 D MoveIn 2007-08-31 Musk
9 D MoveOut 2010-08-13 Musk
So for house d there is a move in and a move out but no recorded movein after that so it should show vacant
my code
SELECT HouseName, Address, currentresident
FROM (SELECT h.*, COALESCE(e.Family, 'Vacant') AS currentresident,
ROW_NUMBER()OVER(PARTITION BY h.HouseName ORDER BY e.Date desc) AS rn
FROM House AS h left JOIN Events AS e ON h.HouseName=e.HouseName)
AS t WHERE t.rn=1
Here is something to try on your larger database.
SELECT HouseName, Address, currentresident
FROM (SELECT h.*,
Case When e.EventName = 'MoveOut' Then 'Vacant'
Else COALESCE(e.Family, 'Vacant') End AS currentresident,
ROW_NUMBER()OVER(PARTITION BY h.HouseName ORDER BY e.Date desc) AS rn
FROM xyzHouse AS h left JOIN xyzEvents AS e ON h.HouseName=e.HouseName)
AS t WHERE t.rn=1
select H.HouseName, H.Address,
Case When ISNULL(e.EventName,'MoveOut')='MoveOut' then'[Vacant]'
Else e.Family end as CurrentResident
from House h
left join Events e on e.id =
(
select max(e2.id) from events e2
where e2.housename = h.housename and e2.EventName in ('MoveIn','MoveOut')
)
SELECT HouseName, Address, currentresident
FROM (SELECT h.*, COALESCE(case when e.EventName='MoveOut' then null else e.Family end, 'Vacant') AS currentresident,
ROW_NUMBER()OVER(PARTITION BY h.HouseName ORDER BY e.Date desc) AS rn
FROM House AS h left JOIN Events AS e ON h.HouseName=e.HouseName)
AS t WHERE t.rn=1
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
Essentially, I have a table that lists an employee's number and the employeeNumber of who they report to.
EmpNum | Name | ReportsTo
---------------------------------
1234 | John Smith | 4523
3245 | Annie Apples | 1234
1532 | Bob Rogers | 3245
6574 | Dong Wong | 1532
Etc. Etc. So Dong Wong's hierarchy would be: He reports to -> 1532 which reports to -> 3245 which reports to -> 1234.
(I'm new to SQL, so clean and understandable solutions would be appreciated)
By doing a join...
select e1.EmpNum, e1.name, e2.EmpNum as BossNum, e2.name as BossName from empTable e1 join empTable e2 on e1.ReportsTo=e2.EmpNum
The join can then be as long as you want, e3, e4... But doing it programatically is probably easier in the front end.
You didn't specify your DBMS so this is standard ANSI SQL
with recursive report_tree as (
select empnum, name, reportsto
from employees
where empnum = 6574
union all
select c.empnum, c.name, c.reportsto
from employees c
join report_tree p on p.reportsto = c.empnum
)
select *
from report_tree;
If you want a "graphical" display, you can do something like this (still standard ANSI SQL):
with recursive report_tree as (
select empnum, name, reportsto, name as report_path, 1 as level
from employee
where empnum = 6574
union all
select c.empnum, c.name, c.reportsto, p.report_path || ' -> ' || c.name, p.level + 1
from employee c
join report_tree p on p.reportsto = c.empnum
)
select *
from report_tree
order by level desc
fetch first 1 row only;
Try this, cte is best for recursion. SO already has many solutions for such problem
create table #emp(
EmpNum int,
Name varchar(50),
ReportsTo int
);
insert into #emp
values
(1234,'John',4523),
(3245,'Annie',1234),
(1532,'Bob',3245),
(6574,'Dong',1532)
with rec as (
select #emp.ReportsTo, #emp.EmpNum, #emp.Name, 1 as level from #emp where Name = 'Bob'
union all
select #emp.ReportsTo, #emp.EmpNum, #emp.Name, level + 1 as level from #emp
inner join rec
on #emp.EmpNum = rec.ReportsTo
)
select ReportsTo, EmpNum, Name, level from rec
where level = (select max(level) from rec)
OPTION (MAXRECURSION 0)
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
If I have rows with this data:
ID |Name |ContractType|
---|------------|------------|
1 |Aaron Shatz | 6-month |
2 |Jim Smith |12-month |
3 |Jim Smith | 6-month |
4 |Mark Johnson|12-month |
I can't use Id to determine which record to use: I have to use ContractType. I want to select all records from a table, but if there are records with the same Name value, I want to pick the 12-month contract record.
The result of the query should be:
ID |Name |ContractType|
---|------------|------------|
1 |Aaron Shatz | 6-month |
2 |Jim Smith |12-month |
4 |Mark Johnson|12-month |
Hard coded version
This solution assumes that there are only two contract types namely 6-month and 12-month. Please scroll to the bottom for dynamic version.
Click here to view the demo in SQL Fiddle.
Script:
CREATE TABLE contracts
(
id INT NOT NULL IDENTITY
, name VARCHAR(30) NOT NULL
, contracttype VARCHAR(30) NOT NULL
);
INSERT INTO contracts (name, contracttype) VALUES
('Aaron Shatz', '6-month'),
('Jim Smith', '12-month'),
('Jim Smith', '12-month'),
('Mark Johnson', '12-month'),
('John Doe', '6-month'),
('Mark Johnson', '6-month'),
('Aaron Shatz', '6-month');
SELECT id
, name
, contracttype
FROM
(
SELECT id
, name
, contracttype
, ROW_NUMBER() OVER(PARTITION BY name ORDER BY contracttype) AS rownum
FROM contracts
) T1
WHERE rownum = 1
ORDER BY id;
Output:
id name contracttype
-- ------------ ------------
1 Aaron Shatz 6-month
2 Jim Smith 12-month
4 Mark Johnson 12-month
5 John Doe 6-month
Dynamic version
This moves the contract type data into a table of its own with a sequence column. Based on how the contract types are ordered, the query will fetch the appropriate records.
Click here to view the demo in SQL Fiddle.
Script:
CREATE TABLE contracts
(
id INT NOT NULL IDENTITY
, name VARCHAR(30) NOT NULL
, contracttypeid INT NOT NULL
);
CREATE TABLE contracttypes
(
id INT NOT NULL IDENTITY
, contracttype VARCHAR(30) NOT NULL
, sequence INT NOT NULL
)
INSERT INTO contracttypes (contracttype, sequence) VALUES
('12-month', 1),
('6-month', 3),
('15-month', 2);
INSERT INTO contracts (name, contracttypeid) VALUES
('Aaron Shatz', 2),
('Jim Smith', 2),
('Jim Smith', 3),
('Mark Johnson', 1),
('John Doe', 2),
('Mark Johnson', 2),
('Aaron Shatz', 2);
SELECT id
, name
, contracttype
FROM
(
SELECT c.id
, c.name
, ct.contracttype
, ROW_NUMBER() OVER(PARTITION BY name ORDER BY ct.sequence) AS rownum
FROM contracts c
LEFT OUTER JOIN contracttypes ct
ON c.contracttypeid = ct.id
) T1
WHERE rownum = 1
ORDER BY id;
Output:
id name contracttype
-- ------------ ------------
1 Aaron Shatz 6-month
3 Jim Smith 15-month
4 Mark Johnson 12-month
5 John Doe 6-month
This works only because the OP has confirmed that only two contract types are possible, and the one he wants (for each contractor) happens to be the one that orders first alphabetically. So a couple of coincidences make this solution straight-forward.
;WITH x AS
(
SELECT ID, Name, ContractType, rn = ROW_NUMBER() OVER
(PARTITION BY Name ORDER BY ContractType)
FROM dbo.some_table
)
SELECT ID, Name, ContractType
FROM x
WHERE rn = 1
ORDER BY ID;
If you need to make this more dynamic, I suppose you could say:
DECLARE #PreferredContractType VARCHAR(32);
SET #PreferredContractType = '12-month';
;WITH x AS
(
SELECT ID, Name, ContractType, rn = ROW_NUMBER() OVER
(PARTITION BY Name ORDER BY CASE ContractType
WHEN #PreferredContractType THEN 1 ELSE 2 END
)
FROM dbo.some_table
)
SELECT ID, Name, ContractType
FROM x
WHERE rn = 1
ORDER BY ID;