I have query to get category and subcategories of same category. I used below query:
Select Id from category where name like '%events%' and deleted = 0 and published = 1
UNION
SELECT Id from category where parentcategoryid = (Select id from category where name like '%events%' and deleted = 0 and published = 1)
I do not want to use UNION, want to use Join only. But not getting how i can achieve. Below is the table structure. Please help me. thanks in Advance
The following might solve your problem:
DECLARE #t TABLE (id int, name varchar(20), parentcatid varchar(200))
insert into #t values(23,'Christmas', 34)
,(29,'Birthday', 34)
,(31,'New year', 34)
,(34,'Events', 0)
,(35,'gfrhrt', 0)
;WITH cte AS(
Select Id from #t
where name like '%events%' --and deleted = 0 and published = 1
),
cte2 AS (
SELECT a.id AS tId, cpa.id, cId2.id AS idPa
from #t a
LEFT JOIN cte AS cId ON cId.Id = a.Id
FULL OUTER JOIN #t AS cPa ON cPa.parentcatid = cId.Id
LEFT JOIN cte cId2 ON cId2.id = cPa.id
WHERE cId.Id IS NOT NULL OR cPa.Id IS NOT NULL
)
SELECT id
FROM cte2
WHERE tId IS NOT NULL
OR id = idPa
The idea is to get all required IDs within the cte and then get all categories, where either ID oder ParentID match the IDs from the cte. However, depending on the size of your table, you might want to add further filters to the cte.
I would like to insert rows into a table using a select statement to query specific data but use data from a different table as part of the insert. EXmaple:
Table A: Clients where data is being queried and copied
Table B: MailOptOut where data is being inserted
I want to insert in MailOptOut table two values, a hardcoded field 'Summer Promotion' and then the acct# from the clients table (client.acct_no)
Here is my code that isn't working:
INSERT INTO PL00.DBO.mailcoptout (MC_NAME, ACCT_NO)
VALUES
('Summer Service Promo', client.acct_no),
('Referral Rewards Doubled', client.acct_no),
('Holiday Decorating 1', client.acct_no),
('Holiday Decorating 2', client.acct_no)
select client.acct_no, mailcoptout.* from plshared.dbo.client left join PL00.DBO.mailcoptout on mailcoptout.ACCT_NO = client.ACCT_NO
where client.U_SOLICIT = 'y'
and client.acct_no = '131335'
and client.INACTIVE <> 'y'
and mailcoptout.MC_NAME is null
Is this what you are trying to do?
INSERT INTO PL00.DBO.mailcoptout (MC_NAME, ACCT_NO)
select x.mc_name, client.acct_no
from plshared.dbo.client c left join
PL00.DBO.mailcoptout mc
on mailcoptout.ACCT_NO = client.ACCT_NO cross join
(select 'Summer Service Promo' as MC_NAME union all,
select 'Referral Rewards Doubled' as MC_NAME union all
select 'Holiday Decorating 1' as MC_NAME union all
select 'Holiday Decorating 2' as MC_NAME
) x
where c.U_SOLICIT = 'y' and
c.acct_no = '131335' and
c.INACTIVE <> 'y' and
mc.MC_NAME is null;
Create and Populate POC tables:
CREATE TABLE #POCSTBL (SCode varchar(10))
INSERT INTO #POCSTBL (SCode)
SElECT 'LC13'
UNION ALL
SELECT 'LC22'
UNION ALL
SELECT 'LC31'
CREATE TABLE #POCLUTBL (ID int Identity (1,1), LC1 varchar(10), LC2 varchar(10), LC3 varchar(10))
INSERT INTO #POCLUTBL (LC1, LC2, LC3)
SELECT 'LC11',NULL,'LC13'
UNION ALL
SELECT 'LC21','LC13',NULL
UNION ALL
SELECT '-','LC31','LC33'
SELECT * FROM #POCSTBL
SELECT * FROM #POCLUTBL
My Initial Try:
SELECT S.SCode, LU.ID FROM #POCSTBL S LEFT JOIN #POCLUTBL LU
ON S.SCode = LU.LC1 OR S.SCode = LU.LC2 OR S.SCode = LU.LC3
Drop #Tbls:
DROP TABLE #POCSTBL
DROP TABLE #POCLUTBL
Desired Output:
SCode ID
LC13 2
LC22 NULL
LC33 3
I want to lookup SCode column from #POCSTBL against #POCLUTBL's
LC2 first, if found, take that ID as output,
if ID not found then LC1, if found, take that ID as output,
if ID not found then LC3, if found, take that ID as output,
if ID not found then ID = NULL
Thank you
Like this:
SELECT
S.SCode,
COALESCE(L1.ID, L2.ID, L3.ID) As ID
FROM #POCSTBL S
LEFT JOIN #POCLUTBL L1 ON S.SCode = L1.LC1
LEFT JOIN #POCLUTBL L2 ON S.SCode = L2.LC2
LEFT JOIN #POCLUTBL L3 ON S.SCode = L3.LC3
Assuming that there are not potential duplicate SCodes in LC1, LC2 or LC3. If there may be, then you will need a more complicated version of this.
Hopefully I'm missing a simple solution to this.
I have two tables. One contains a list of companies. The second contains a list of publishers. The mapping between the two is many to many. What I would like to do is bundle or group all of the companies in table A which have any relationship to a publisher in table B and vise versa.
The final result would look something like this (GROUPID is the key field). Row 1 and 2 are in the same group because they share the same company. Row 3 is in the same group because the publisher Y was already mapped over to company A. Row 4 is in the group because Company B was already mapped to group 1 through Publisher Y.
Said simply, any time there is any kind of shared relationship across Company and Publisher, that pair should be assigned to the same group.
ROW GROUPID Company Publisher
1 1 A Y
2 1 A X
3 1 B Y
4 1 B Z
5 2 C W
6 2 C P
7 2 D W
Fiddle
Update:
My bounty version: Given the table in the fiddle above of simply Company and Publisher pairs, populate the GROUPID field above. Think of it as creating a Family ID that encompasses all related parents/children.
SQL Server 2012
I thought about using recursive CTE, but, as far as I know, it's not possible in SQL Server to use UNION to connect anchor member and a recursive member of recursive CTE (I think it's possible to do in PostgreSQL), so it's not possible to eliminate duplicates.
declare #i int
with cte as (
select
GroupID,
row_number() over(order by Company) as rn
from Table1
)
update cte set GroupID = rn
select #i = ##rowcount
-- while some rows updated
while #i > 0
begin
update T1 set
GroupID = T2.GroupID
from Table1 as T1
inner join (
select T2.Company, min(T2.GroupID) as GroupID
from Table1 as T2
group by T2.Company
) as T2 on T2.Company = T1.Company
where T1.GroupID > T2.GroupID
select #i = ##rowcount
update T1 set
GroupID = T2.GroupID
from Table1 as T1
inner join (
select T2.Publisher, min(T2.GroupID) as GroupID
from Table1 as T2
group by T2.Publisher
) as T2 on T2.Publisher = T1.Publisher
where T1.GroupID > T2.GroupID
-- will be > 0 if any rows updated
select #i = #i + ##rowcount
end
;with cte as (
select
GroupID,
dense_rank() over(order by GroupID) as rn
from Table1
)
update cte set GroupID = rn
sql fiddle demo
I've also tried a breadth first search algorithm. I thought it could be faster (it's better in terms of complexity), so I'll provide a solution here. I've found that it's not faster than SQL approach, though:
declare #Company nvarchar(2), #Publisher nvarchar(2), #GroupID int
declare #Queue table (
Company nvarchar(2), Publisher nvarchar(2), ID int identity(1, 1),
primary key(Company, Publisher)
)
select #GroupID = 0
while 1 = 1
begin
select top 1 #Company = Company, #Publisher = Publisher
from Table1
where GroupID is null
if ##rowcount = 0 break
select #GroupID = #GroupID + 1
insert into #Queue(Company, Publisher)
select #Company, #Publisher
while 1 = 1
begin
select top 1 #Company = Company, #Publisher = Publisher
from #Queue
order by ID asc
if ##rowcount = 0 break
update Table1 set
GroupID = #GroupID
where Company = #Company and Publisher = #Publisher
delete from #Queue where Company = #Company and Publisher = #Publisher
;with cte as (
select Company, Publisher from Table1 where Company = #Company and GroupID is null
union all
select Company, Publisher from Table1 where Publisher = #Publisher and GroupID is null
)
insert into #Queue(Company, Publisher)
select distinct c.Company, c.Publisher
from cte as c
where not exists (select * from #Queue as q where q.Company = c.Company and q.Publisher = c.Publisher)
end
end
sql fiddle demo
I've tested my version and Gordon Linoff's to check how it's perform. It looks like CTE is much worse, I couldn't wait while it's complete on more than 1000 rows.
Here's sql fiddle demo with random data. My results were:
128 rows:
my RBAR solution: 190ms
my SQL solution: 27ms
Gordon Linoff's solution: 958ms
256 rows:
my RBAR solution: 560ms
my SQL solution: 1226ms
Gordon Linoff's solution: 45371ms
It's random data, so results may be not very consistent. I think timing could be changed by indexes, but don't think it could change a whole picture.
old version - using temporary table, just calculating GroupID without touching initial table:
declare #i int
-- creating table to gather all possible GroupID for each row
create table #Temp
(
Company varchar(1), Publisher varchar(1), GroupID varchar(1),
primary key (Company, Publisher, GroupID)
)
-- initializing it with data
insert into #Temp (Company, Publisher, GroupID)
select Company, Publisher, Company
from Table1
select #i = ##rowcount
-- while some rows inserted into #Temp
while #i > 0
begin
-- expand #Temp in both directions
;with cte as (
select
T2.Company, T1.Publisher,
T1.GroupID as GroupID1, T2.GroupID as GroupID2
from #Temp as T1
inner join #Temp as T2 on T2.Company = T1.Company
union
select
T1.Company, T2.Publisher,
T1.GroupID as GroupID1, T2.GroupID as GroupID2
from #Temp as T1
inner join #Temp as T2 on T2.Publisher = T1.Publisher
), cte2 as (
select
Company, Publisher,
case when GroupID1 < GroupID2 then GroupID1 else GroupID2 end as GroupID
from cte
)
insert into #Temp
select Company, Publisher, GroupID
from cte2
-- don't insert duplicates
except
select Company, Publisher, GroupID
from #Temp
-- will be > 0 if any row inserted
select #i = ##rowcount
end
select
Company, Publisher,
dense_rank() over(order by min(GroupID)) as GroupID
from #Temp
group by Company, Publisher
=> sql fiddle example
Your problem is a graph-walking problem of finding connected subgraphs. It is a little more challenging because your data structure has two types of nodes ("companies" and "pubishers") rather than one type.
You can solve this with a single recursive CTE. The logic is as follows.
First, convert the problem into a graph with only one type of node. I do this by making the nodes companies and the edges linkes between companies, using the publisher information. This is just a join:
select t1.company as node1, t2.company as node2
from table1 t1 join
table1 t2
on t1.publisher = t2.publisher
)
(For efficiency sake, you could also add t1.company <> t2.company but that is not strictly necessary.)
Now, this is a "simple" graph walking problem, where a recursive CTE is used to create all connections between two nodes. The recursive CTE walks through the graph using join. Along the way, it keeps a list of all nodes visited. In SQL Server, this needs to be stored in a string.
The code needs to ensure that it doesn't visit a node twice for a given path, because this can result in infinite recursion (and an error). If the above is called edges, the CTE that generates all pairs of connected nodes looks like:
cte as (
select e.node1, e.node2, cast('|'+e.node1+'|'+e.node2+'|' as varchar(max)) as nodes,
1 as level
from edges e
union all
select c.node1, e.node2, c.nodes+e.node2+'|', 1+c.level
from cte c join
edges e
on c.node2 = e.node1 and
c.nodes not like '|%'+e.node2+'%|'
)
Now, with this list of connected nodes, assign each node the minimum of all the nodes it is connected to, including itself. This serves as an identifier of connected subgraphs. That is, all companies connected to each other via the publishers will have the same minimum.
The final two steps are to enumerate this minimum (as the GroupId) and to join the GroupId back to the original data.
The full (and I might add tested) query looks like:
with edges as (
select t1.company as node1, t2.company as node2
from table1 t1 join
table1 t2
on t1.publisher = t2.publisher
),
cte as (
select e.node1, e.node2,
cast('|'+e.node1+'|'+e.node2+'|' as varchar(max)) as nodes,
1 as level
from edges e
union all
select c.node1, e.node2,
c.nodes+e.node2+'|',
1+c.level
from cte c join
edges e
on c.node2 = e.node1 and
c.nodes not like '|%'+e.node2+'%|'
),
nodes as (
select node1,
(case when min(node2) < node1 then min(node2) else node1 end
) as grp
from cte
group by node1
)
select t.company, t.publisher, grp.GroupId
from table1 t join
(select n.node1, dense_rank() over (order by grp) as GroupId
from nodes n
) grp
on t.company = grp.node1;
Note that this works on finding any connected subgraphs. It does not assume that any particular number of levels.
EDIT:
The question of performance for this is vexing. At a minimum, the above query will run better with an index on Publisher. Better yet is to take #MikaelEriksson's suggestion, and put the edges in a separate table.
Another question is whether you look for equivalency classes among the Companies or the Publishers. I took the approach of using Companies, because I think that has better "explanability" (my inclination to respond was based on numerous comments that this could not be done with CTEs).
I am guessing that you could get reasonable performance from this, although that requires more knowledge of your data and system than provided in the OP. It is quite likely, though, that the best performance will come from a multiple query approach.
Here is my solution SQL Fiddle
The nature of the relationships require looping as I figure.
Here is the SQL:
--drop TABLE Table1
CREATE TABLE Table1
([row] int identity (1,1),GroupID INT NULL,[Company] varchar(2), [Publisher] varchar(2))
;
INSERT INTO Table1
(Company, Publisher)
select
left(newid(), 2), left(newid(), 2)
declare #i int = 1
while #i < 8
begin
;with cte(Company, Publisher) as (
select
left(newid(), 2), left(newid(), 2)
from Table1
)
insert into Table1(Company, Publisher)
select distinct c.Company, c.Publisher
from cte as c
where not exists (select * from Table1 as t where t.Company = c.Company and t.Publisher = c.Publisher)
set #i = #i + 1
end;
CREATE NONCLUSTERED INDEX IX_Temp1 on Table1 (Company)
CREATE NONCLUSTERED INDEX IX_Temp2 on Table1 (Publisher)
declare #counter int=0
declare #row int=0
declare #lastnullcount int=0
declare #currentnullcount int=0
WHILE EXISTS (
SELECT *
FROM Table1
where GroupID is null
)
BEGIN
SET #counter=#counter+1
SET #lastnullcount =0
SELECT TOP 1
#row=[row]
FROM Table1
where GroupID is null
order by [row] asc
SELECT #currentnullcount=count(*) from table1 where groupid is null
WHILE #lastnullcount <> #currentnullcount
BEGIN
SELECT #lastnullcount=count(*)
from table1
where groupid is null
UPDATE Table1
SET GroupID=#counter
WHERE [row]=#row
UPDATE t2
SET t2.GroupID=#counter
FROM Table1 t1
INNER JOIN Table1 t2 on t1.Company=t2.Company
WHERE t1.GroupID=#counter
AND t2.GroupID IS NULL
UPDATE t2
SET t2.GroupID=#counter
FROM Table1 t1
INNER JOIN Table1 t2 on t1.publisher=t2.publisher
WHERE t1.GroupID=#counter
AND t2.GroupID IS NULL
SELECT #currentnullcount=count(*)
from table1
where groupid is null
END
END
SELECT * FROM Table1
Edit:
Added indexes as I would expect on the real table and be more in line with the other data sets Roman is using.
You are trying to find all of the connected components of your graph, which can only be done iteratively. If you know the maximum width of any connected component (i.e. the maximum number of links you will have to take from one company/publisher to another), you could in principle do it something like this:
SELECT
MIN(x2.groupID) AS groupID,
x1.Company,
x1.Publisher
FROM Table1 AS x1
INNER JOIN (
SELECT
MIN(x2.Company) AS groupID,
x1.Company,
x1.Publisher
FROM Table1 AS x1
INNER JOIN Table1 AS x2
ON x1.Publisher = x2.Publisher
GROUP BY
x1.Publisher,
x1.Company
) AS x2
ON x1.Company = x2.Company
GROUP BY
x1.Publisher,
x1.Company;
You have to keep nesting the subquery (alternating joins on Company and Publisher, and with the deepest subquery saying MIN(Company) rather than MIN(groupID)) to the maximum iteration depth.
I don't really recommend this, though; it would be cleaner to do this outside of SQL.
Disclaimer: I don't know anything about SQL Server 2012 (or any other version); it may have some kind of additional scripting ability to let you do this iteration dynamically.
This is a recursive solution, using XML:
with a as ( -- recursive result, containing shorter subsets and duplicates
select cast('<c>' + company + '</c>' as xml) as companies
,cast('<p>' + publisher + '</p>' as xml) as publishers
from Table1
union all
select a.companies.query('for $c in distinct-values((for $i in /c return string($i),
sql:column("t.company")))
order by $c
return <c>{$c}</c>')
,a.publishers.query('for $p in distinct-values((for $i in /p return string($i),
sql:column("t.publisher")))
order by $p
return <p>{$p}</p>')
from a join Table1 t
on ( a.companies.exist('/c[text() = sql:column("t.company")]') = 0
or a.publishers.exist('/p[text() = sql:column("t.publisher")]') = 0)
and ( a.companies.exist('/c[text() = sql:column("t.company")]') = 1
or a.publishers.exist('/p[text() = sql:column("t.publisher")]') = 1)
), b as ( -- remove the shorter versions from earlier steps of the recursion and the duplicates
select distinct -- distinct cannot work on xml types, hence cast to nvarchar
cast(companies as nvarchar) as companies
,cast(publishers as nvarchar) as publishers
,DENSE_RANK() over(order by cast(companies as nvarchar), cast(publishers as nvarchar)) as groupid
from a
where not exists (select 1 from a as s -- s is a proper subset of a
where (cast('<s>' + cast(s.companies as varchar)
+ '</s><a>' + cast(a.companies as varchar) + '</a>' as xml)
).value('if((count(/s/c) > count(/a/c))
and (some $s in /s/c/text() satisfies
(some $a in /a/c/text() satisfies $s = $a))
) then 1 else 0', 'int') = 1
)
and not exists (select 1 from a as s -- s is a proper subset of a
where (cast('<s>' + cast(s.publishers as nvarchar)
+ '</s><a>' + cast(a.publishers as nvarchar) + '</a>' as xml)
).value('if((count(/s/p) > count(/a/p))
and (some $s in /s/p/text() satisfies
(some $a in /a/p/text() satisfies $s = $a))
) then 1 else 0', 'int') = 1
)
), c as ( -- cast back to xml
select cast(companies as xml) as companies
,cast(publishers as xml) as publishers
,groupid
from b
)
select Co.company.value('(./text())[1]', 'varchar') as company
,Pu.publisher.value('(./text())[1]', 'varchar') as publisher
,c.groupid
from c
cross apply companies.nodes('/c') as Co(company)
cross apply publishers.nodes('/p') as Pu(publisher)
where exists(select 1 from Table1 t -- restrict to only the combinations that exist in the source
where t.company = Co.company.value('(./text())[1]', 'varchar')
and t.publisher = Pu.publisher.value('(./text())[1]', 'varchar')
)
The set of companies and the set of publishers are kept in XML fields in the intermediate steps, and there is some casting between xml and nvarchar necessary due to some limitations of SQL Server (like not being able to group or use distinct on XML columns.
Bit late to the challenge, and since SQLFiddle seems to be down ATM I'll have to guess your data-structures. Nevertheless, it seemed like a fun challenge (and it was =) so here's what I made from it :
Setup:
IF OBJECT_ID('t_link') IS NOT NULL DROP TABLE t_link
IF OBJECT_ID('t_company') IS NOT NULL DROP TABLE t_company
IF OBJECT_ID('t_publisher') IS NOT NULL DROP TABLE t_publisher
IF OBJECT_ID('tempdb..#link_A') IS NOT NULL DROP TABLE #link_A
IF OBJECT_ID('tempdb..#link_B') IS NOT NULL DROP TABLE #link_B
GO
CREATE TABLE t_company ( company_id int IDENTITY(1, 1) NOT NULL PRIMARY KEY,
company_name varchar(100) NOT NULL)
GO
CREATE TABLE t_publisher (publisher_id int IDENTITY(1, 1) NOT NULL PRIMARY KEY,
publisher_name varchar(100) NOT NULL)
CREATE TABLE t_link (company_id int NOT NULL FOREIGN KEY (company_id) REFERENCES t_company (company_id),
publisher_id int NOT NULL FOREIGN KEY (publisher_id) REFERENCES t_publisher (publisher_id),
PRIMARY KEY (company_id, publisher_id),
group_id int NULL
)
GO
-- example content
-- ROW GROUPID Company Publisher
--1 1 A Y
--2 1 A X
--3 1 B Y
--4 1 B Z
--5 2 C W
--6 2 C P
--7 2 D W
INSERT t_company (company_name) VALUES ('A'), ('B'), ('C'), ('D')
INSERT t_publisher (publisher_name) VALUES ('X'), ('Y'), ('Z'), ('W'), ('P')
INSERT t_link (company_id, publisher_id)
SELECT company_id, publisher_id
FROM t_company, t_publisher
WHERE (company_name = 'A' AND publisher_name = 'Y')
OR (company_name = 'A' AND publisher_name = 'X')
OR (company_name = 'B' AND publisher_name = 'Y')
OR (company_name = 'B' AND publisher_name = 'Z')
OR (company_name = 'C' AND publisher_name = 'W')
OR (company_name = 'C' AND publisher_name = 'P')
OR (company_name = 'D' AND publisher_name = 'W')
GO
/*
-- volume testing
TRUNCATE TABLE t_link
DELETE t_company
DELETE t_publisher
DECLARE #company_count int = 1000,
#publisher_count int = 450,
#links_count int = 800
INSERT t_company (company_name)
SELECT company_name = Convert(varchar(100), NewID())
FROM master.dbo.fn_int_list(1, #company_count)
UPDATE STATISTICS t_company
INSERT t_publisher (publisher_name)
SELECT publisher_name = Convert(varchar(100), NewID())
FROM master.dbo.fn_int_list(1, #publisher_count)
UPDATE STATISTICS t_publisher
-- Random links between the companies & publishers
DECLARE #count int
SELECT #count = 0
WHILE #count < #links_count
BEGIN
SELECT TOP 30 PERCENT row_id = IDENTITY(int, 1, 1), company_id = company_id + 0
INTO #link_A
FROM t_company
ORDER BY NewID()
SELECT TOP 30 PERCENT row_id = IDENTITY(int, 1, 1), publisher_id = publisher_id + 0
INTO #link_B
FROM t_publisher
ORDER BY NewID()
INSERT TOP (#links_count - #count) t_link (company_id, publisher_id)
SELECT A.company_id,
B.publisher_id
FROM #link_A A
JOIN #link_B B
ON A.row_id = B.row_id
WHERE NOT EXISTS ( SELECT *
FROM t_link old
WHERE old.company_id = A.company_id
AND old.publisher_id = B.publisher_id)
SELECT #count = #count + ##ROWCOUNT
DROP TABLE #link_A
DROP TABLE #link_B
END
*/
Actual grouping:
IF OBJECT_ID('tempdb..#links') IS NOT NULL DROP TABLE #links
GO
-- apply grouping
-- init
SELECT row_id = IDENTITY(int, 1, 1),
company_id,
publisher_id,
group_id = 0
INTO #links
FROM t_link
-- don't see an index that would be actually helpful here right-away, using row_id to avoid HEAP
CREATE CLUSTERED INDEX idx0 ON #links (row_id)
--CREATE INDEX idx1 ON #links (company_id)
--CREATE INDEX idx2 ON #links (publisher_id)
UPDATE #links
SET group_id = row_id
-- start grouping
WHILE ##ROWCOUNT > 0
BEGIN
UPDATE #links
SET group_id = new_group_id
FROM #links upd
CROSS APPLY (SELECT new_group_id = Min(group_id)
FROM #links new
WHERE new.company_id = upd.company_id
OR new.publisher_id = upd.publisher_id
) x
WHERE upd.group_id > new_group_id
-- select * from #links
END
-- remove 'holes'
UPDATE #links
SET group_id = (SELECT COUNT(DISTINCT o.group_id)
FROM #links o
WHERE o.group_id <= upd.group_id)
FROM #links upd
GO
UPDATE t_link
SET group_id = new.group_id
FROM t_link upd
LEFT OUTER JOIN #links new
ON new.company_id = upd.company_id
AND new.publisher_id = upd.publisher_id
GO
SELECT row = ROW_NUMBER() OVER (ORDER BY group_id, company_name, publisher_name),
l.group_id,
c.company_name, -- c.company_id,
p.publisher_name -- , p.publisher_id
from t_link l
JOIN t_company c
ON l.company_id = c.company_id
JOIN t_publisher p
ON p.publisher_id = l.publisher_id
ORDER BY 1
At first sight this approach hasn't been tried yet by anyone else, interesting to see how this can be done in a variety of ways... (preferred not to read them upfront as it would spoil the puzzle =)
Results look as expected (as far as I understand the requirements and the example) and performance isn't too shabby either although there is no real indication on the amount of records this should work on; not sure how it would scale but don't expect too many problems either...
I have 3 tables that I'm trying to create a query from:
Table 1 (iuieEmployee) ->position number
Table 2 (jbEmployeeH1BInfo) -> position number, LCA number, start date
Table 3 (jbEmployeeLCA) -> LCA number
Table 4 (jbInternationsl) -> Main demographic table
I have a query that works fine where there's only 1 record in each table, but tables 2 and 3 can have multiple records. I want it to find the record with he most recent start date and verify that there is a matching LCA number in the 3rd table and a matching position number int he first table and show me any records where this isn't the case. How can I accomplish this? I currently have:
SELECT DISTINCT jbInternational.idnumber, jbInternational.lastname, jbInternational.firstname, jbInternational.midname,
jbInternational.campus, jbInternational.universityid, jbInternational.sevisid, jbInternational.citizenship,
jbInternational.immigrationstatus, jbEmployeeH1BInfo.lcaNumber AS lcaNumber1, jbEmployeeLCA.lcaNumber AS lcaNumber2
FROM (select jbEmployeeH1BInfo.idnumber, MAX(jbEmployeeH1BInfo.approvalStartDate) AS MaxDateStamp FROM [internationalservices].[dbo].jbEmployeeH1BInfo GROUP BY idnumber ) my
INNER JOIN [internationalservices].[dbo].jbEmployeeH1BInfo WITH (nolock) ON my.idnumber=jbEmployeeH1BInfo.idnumber AND my.MaxDateStamp=jbEmployeeH1BInfo.approvalStartDate
INNER JOIN [internationalservices].[dbo].jbInternational WITH (nolock) ON jbInternational.idnumber=jbEmployeeH1BInfo.idnumber
inner join [internationalservices].[dbo].jbEmployeeLCA ON jbInternational.idnumber = jbEmployeeLCA.idnumber
WHERE jbInternational.idnumber not in(
SELECT DISTINCT jbInternational.idnumber
FROM (select distinct jbEmployeeH1BInfo.idnumber, MAX(jbEmployeeH1BInfo.approvalStartDate) AS MaxDateStamp
FROM [internationalservices].[dbo].jbEmployeeH1BInfo GROUP BY idnumber ) my
INNER JOIN [internationalservices].[dbo].jbEmployeeH1BInfo WITH (nolock) ON my.idnumber=jbEmployeeH1BInfo.idnumber AND my.MaxDateStamp=jbEmployeeH1BInfo.approvalStartDate
INNER JOIN [internationalservices].[dbo].jbInternational WITH (nolock) ON jbInternational.idnumber=jbEmployeeH1BInfo.idnumber
inner join [internationalservices].[dbo].jbEmployeeLCA ON jbInternational.idnumber = jbEmployeeLCA.idnumber
AND jbEmployeeH1BInfo.lcaNumber = jbEmployeeLCA.lcaNumber)
Table Schema:
create table iuieEmployee(idnumber int, POS_NBR varchar(8));
insert into iuieEmployee values(123456, '470V13');
insert into iuieEmployee values(123457, '98X000');
insert into iuieEmployee values(123458, '98X000');
insert into iuieEmployee values(123455, '98X000');
create table jbEmployeeH1BInfo (idnumber int, approvalStartDate smalldatetime, lcaNumber varchar(20), positionNumber varchar(200));
insert into jbEmployeeH1BInfo values (123456, 07/01/2012, '1-200-3000', '98X000');
insert into jbEmployeeH1BInfo values (123456, 07/30/2013, '1-200-4000', '470V13');
insert into jbEmployeeH1BInfo values (123457, 07/01/2012, '1-200-5000', '98X000');
insert into jbEmployeeH1BInfo values (123458, 07/01/2012, '1-200-6000', '98X000');
insert into jbEmployeeH1BInfo values (123455, 07/30/2014, '1-200-7000', '98X000');
insert into jbEmployeeH1BInfo values (123455, 07/01/2012, '1-200-8000', '470V13');
create table jbEmployeeLCA (idnumber int, lcaNumber varchar(20));
insert into jbEmployeeLCA values (123456, 1-200-3000);
insert into jbEmployeeLCA values (123456, 1-200-4111);
insert into jbEmployeeLCA values (123457, 1-200-5000);
insert into jbEmployeeLCA values (123458, 1-200-6000);
insert into jbEmployeeLCA values (123455, 1-200-7000);
insert into jbEmployeeLCA values (123455, 1-200-8000);
create table jbInternational(idnumber int);
insert into jbInternational values(123456);
insert into jbInternational values(123457);
insert into jbInternational values(123458);
insert into jbInternational values(123455);
Should only return 1 line:
123456, 07/30/2013, '1-200-4000'
but is instead returning two lines:
123456, 07/30/2013, '1-200-4000 (not matching 1-200-4111)
123456, 07/30/2013, '1-200-4000 (not matching 1-200-3000)
It shouldn't return the second row because the position number with the -3000 lca number doesn't have the most current date.
Your explanation is hard to understand. I guess if you could explain it well, then you could probably write the query yourself. Here's what I think you meant:
Employee contains the main records.
You want to find all idnumbers such that
idnumber is in International
the H1BInfo record with the most recent approvalStartDate does not have an LCA number matching the LCA record
The first thing to do is to simplify that H1BInfo table. We are only looking for the rows with the most recent approvalStartDate. We can do that by partitioning by idnumber and ordering by approvalStartDate:
with rankedH1BInfo as (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY jbEmployeeH1BInfo.idnumber
ORDER BY jbEmployeeH1BInfo.approvalStartDate desc) as r
FROM [internationalservices].[dbo].jbEmployeeH1BInfo
)
Let's only get the first row of each partition:
, MostRecentH1BInfo as (
SELECT * FROM rankedH1BInfo
WHERE r = 1
)
Now we can do the join to find all the good ones:
, goodIDs as (
SELECT i.idnumber
FROM [internationalservices].[dbo].jbInternational i WITH (NOLOCK)
JOIN [internationalservices].[dbo].jbEmployeeLCA l WITH (NOLOCK) on l.idnumber = i.idnumber
JOIN MostRecentH1BInfo h WITH (NOLOCK) on h.idnumber = i.idnumber
JOIN iuieEmployee e WITH (NOLOCK) on e.positionNumber = h.positionNumber
WHERE h.lcaNumber = l.lcaNumber
)
To put it all together and get the ones where this is false:
with rankedH1BInfo as (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY jbEmployeeH1BInfo.idnumber
ORDER BY jbEmployeeH1BInfo.approvalStartDate desc) as r
FROM [internationalservices].[dbo].jbEmployeeH1BInfo
), MostRecentH1BInfo as (
SELECT * FROM rankedH1BInfo
WHERE r = 1
), goodIDs as (
SELECT i.idnumber
FROM [internationalservices].[dbo].jbInternational i WITH (NOLOCK)
JOIN [internationalservices].[dbo].jbEmployeeLCA l WITH (NOLOCK) on l.idnumber = i.idnumber
JOIN MostRecentH1BInfo h WITH (NOLOCK) on h.idnumber = i.idnumber
JOIN iuieEmployee e WITH (NOLOCK) on e.positionNumber = h.positionNumber
WHERE h.lcaNumber = l.lcaNumber
)
SELECT DISTINCT jbInternational.idnumber, jbInternational.lastname, jbInternational.firstname, jbInternational.midname,
jbInternational.campus, jbInternational.universityid, jbInternational.sevisid, jbInternational.citizenship,
jbInternational.immigrationstatus, jbEmployeeH1BInfo.lcaNumber AS lcaNumber1, jbEmployeeLCA.lcaNumber AS lcaNumber2
FROM (select jbEmployeeH1BInfo.idnumber, MAX(jbEmployeeH1BInfo.approvalStartDate) AS MaxDateStamp FROM [internationalservices].[dbo].jbEmployeeH1BInfo GROUP BY idnumber ) my
INNER JOIN [internationalservices].[dbo].jbEmployeeH1BInfo WITH (nolock) ON my.idnumber=jbEmployeeH1BInfo.idnumber AND my.MaxDateStamp=jbEmployeeH1BInfo.approvalStartDate
INNER JOIN [internationalservices].[dbo].jbInternational WITH (nolock) ON jbInternational.idnumber=jbEmployeeH1BInfo.idnumber
inner join [internationalservices].[dbo].jbEmployeeLCA ON jbInternational.idnumber = jbEmployeeLCA.idnumber
WHERE jbInternational.idnumber not in (select idnumber from goodIDs)