Multiple table outer join when any one table may have results - sql-server

I have multiple organizational (6) tables that have data about membership. An individual may be a member on 0 or more organizations. This is an example of common fields from table schema (example only not really) idIndividual,name,address
I want the idIndividual and address for a given name
Select name, address,idIndividual
from tbl1,tbl2,tbl3,tbl4,tbl5,tbl6
where name = 'FOO'
The person may be in none or all of the tables, I'm lost with this one, any help would greatly be appreciated

Use a UNION instead
WITH cte AS
(
SELECT name, address,idIndividual
FROM tbl1
UNION
SELECT name, address,idIndividual
FROM tbl2
UNION
SELECT name, address,idIndividual
FROM tbl3
UNION
SELECT name, address,idIndividual
FROM tbl4
UNION
SELECT name, address,idIndividual
FROM tbl5
UNION
SELECT name, address,idIndividual
FROM tbl6
)
SELECT * FROM cte WHERE name = 'FOO'

Related

SQL - Find orders with certain item combo(from another table)

I have these 3 tables - CodesetList, Rule, Order:
In the first row of the Rule table, if a client ordered any of the CODE_ID under CODESET_ID=12 and any of the CODE_ID under CODESET_ID = 7 on the same service date(DAYS_FROM_Service=0), I want to find the transaction in Order table. Ultimately, if a client orders a combination of two items(Rule table), we want to deny the order according to the rule table.
Sorry, I'm bad at explaining this.
Rule:
CodesetList(note that ABC0274 is under Codeset_ID 9 & 10 and this is expected):
Order:
-- Create CodsetList table
SELECT '7' as CODESET_ID, 'ABC0210' as CODE_ID INTO #CodesetList UNION ALL
SELECT '8','ABC0220' UNION ALL
SELECT '8','ABC0230' UNION ALL
SELECT '8','ABC0240' UNION ALL
SELECT '8','ABC0250' UNION ALL
SELECT '8','ABC0260' UNION ALL
SELECT '9','ABC0270' UNION ALL
SELECT '9','ABC0272' UNION ALL
SELECT '9','ABC0274' UNION ALL
SELECT '9','ABC0273' UNION ALL
SELECT '10','ABC0274' UNION ALL
SELECT '11','ABC0277' UNION ALL
SELECT '12','ABC0330' UNION ALL
SELECT '83','ABC5110' UNION ALL
SELECT '83','ABC5120' UNION ALL
SELECT '83','ABC5130' UNION ALL
SELECT '83','ABC5140'
-- Create Rule table
SELECT '12' as TARGET_CODESET_ID, '7' as DENIAL_CODESET_ID, '2' as DIRECTION_FROM_DOS, '0' as DAYS_FROM_Service INTO #Rule UNION ALL
SELECT '83','7','1','365' UNION ALL
SELECT '7','8','2','0' UNION ALL
SELECT '7','9','2','0'
-- Create Order table
SELECT 'C3340' as ClientID, CAST('2019-10-12' AS DATE) as Service_Date, 'ABC0210' as CODE_ID INTO #Order UNION ALL
SELECT 'C3340',CAST('2019-10-12' AS DATE),'ABC0220' UNION ALL
SELECT 'C3340',CAST('2018-10-23' AS DATE),'ABC0272' UNION ALL
SELECT 'C3340',CAST('2019-10-09' AS DATE),'ABC0220' UNION ALL
SELECT 'C7646',CAST('2019-11-07' AS DATE),'ABC5110' UNION ALL
SELECT 'C7646',CAST('2019-05-07' AS DATE),'ABC0210' UNION ALL
SELECT 'C5376',CAST('2018-12-02' AS DATE),'ABC8411' UNION ALL
SELECT 'C5376',CAST('2018-10-18' AS DATE),'ABC8411' UNION ALL
SELECT 'C5376',CAST('2018-08-06' AS DATE),'ABC8161' UNION ALL
SELECT 'C9873',CAST('2019-01-06' AS DATE),'ABC6517' UNION ALL
SELECT 'C9873',CAST('2019-01-28' AS DATE),'ABC7784' UNION ALL
SELECT 'C9873',CAST('2019-03-05' AS DATE),'ABC8110'
Here's my poor attempt.
It only addresses one of the rules (row 3 from Rule table: Target=7, Denial=8)
And it also ignores the Days_From_Service rule.
SELECT *
FROM (SELECT *
FROM #Order AS o
WHERE o.CODE_ID IN(SELECT CODE_ID FROM #CodesetList WHERE CODESET_ID=7)) AS T
INNER JOIN
(SELECT *
FROM #Order AS o
WHERE o.CODE_ID IN(SELECT CODE_ID FROM #CodesetList WHERE CODESET_ID=8)) AS D ON T.ClientID=D.ClientID AND T.Service_Date=D.Service_Date;

Permutations of multiple data items in T-SQL

I have some data in a base table equivalent to:
EmployeeID,Department,Role,Location
-----------------------------------
001,HR,Support,Bristol
002,Banking,Partner,Sheffield
etc.
I then have an additional three tables that contain any additional department, role or location when it is applicable to an individual, but these are simply populated with the EmployeeID and additional piece of information, e.g.
EmployeeID,Location
-------------------
001,London
Where it is populated, I need to take every permutation, for example if the person had an additional location of London, as above, I'd expect to generate two rows:
001,HR,Support,Bristol
001,HR,Support,London
If the person had an additional role as a secretary, I'd expect four rows, however:
001,HR,Support,Bristol
001,HR,Support,London
001,HR,Secretary,Bristol
001,HR,Secretary,London
For clarity, I'd expect to see any record in the base table that does not match in the multiple table too. So fundamentally in the above example I'd expect five rows:
001,HR,Support,Bristol
001,HR,Support,London
001,HR,Secretary,Bristol
001,HR,Secretary,London
002,Banking,Partner,Sheffield
Hopefully that makes some sense
Thanks in advance.
with departmentX as
(
select EmployeeID, Department from Employee
union
select EmployeeId, Department from Department
),
roleX as
(
select EmployeeID, Role from Employee
union
select EmployeeId, Role from Role
),
locationX as
(
select EmployeeID, Location from Employee
union
select EmployeeId, Location from Location
)
select d.EmployeeId, d.department, r.Role, l.Location
from
departmentX d join roleX r
on d.EmployeeId = r.EMployeeId
join locationX l on d.employeeId = l.EmployeeId
I think I've got an solution - you can do it by using small steps:
1) Find every Departments for the employees
SELECT EmployeeID, Department FROM dbo.additionalDepartments
UNION
SELECT EmployeeID, Department FROM dbo.baseTable
2) Find every Roles for the employees
SELECT EmployeeID, Role FROM dbo.additionalRoles
UNION
SELECT EmployeeID, Role FROM dbo.baseTable
3) Find every Locations for the employees
SELECT EmployeeID, Location FROM dbo.additionalLocations
UNION
SELECT EmployeeID, Location FROM dbo.baseTable
4) Join them all :)
SELECT D.EmployeeID, D.Department, R.Role, L.Location FROM
(
SELECT EmployeeID, Department FROM dbo.additionalDepartments
UNION
SELECT EmployeeID, Department FROM dbo.baseTable
) D
INNER JOIN
(
SELECT EmployeeID, Role FROM dbo.additionalRoles
UNION
SELECT EmployeeID, Role FROM dbo.baseTable
) R ON D.EmployeeID = R.EmployeeID
INNER JOIN
(
SELECT EmployeeID, Location FROM dbo.additionalLocations
UNION
SELECT EmployeeID, Location FROM dbo.baseTable
) L ON D.EmployeeID = L.EmployeeID
Please check my SQL Fiddle. I've built your scenario at http://sqlfiddle.com/#!18/756fd/2/0

Is it possible to improve sort perfomance of ID-based list of rows?

Consider following example:
SET NOCOUNT ON;
CREATE TABLE #Users
(
ID INT IDENTITY(1,1),
Name VARCHAR(50)
);
CREATE CLUSTERED INDEX IDX_C_Users_UserID ON #Users(ID);
-- CREATE INDEX IDX_Users_Name ON #Users(Name); -- It doesn't work.
CREATE TABLE #Towns
(
ID INT IDENTITY(1,1),
Name VARCHAR(50)
);
CREATE CLUSTERED INDEX IDX_C_Towns_UserID ON #Towns(ID)
CREATE TABLE #BeenHere
(
ID INT IDENTITY(1,1), -- for some business reason we can't use clustered index on them
UserID INT,
TownID INT
);
CREATE UNIQUE INDEX IDX_BEEN_THERE ON #BeenHere(TownID, UserID);
INSERT INTO #Towns
SELECT Prefix+Suffix FROM (
SELECT Prefix, Suffix FROM
(SELECT 'China' UNION ALL
SELECT 'Ham' UNION ALL
SELECT 'Chicken' UNION ALL
SELECT 'Great' UNION ALL
SELECT 'Loud'
) as A(Prefix)
CROSS JOIN
(SELECT 'town' UNION ALL
SELECT 'water' UNION ALL
SELECT ' City' UNION ALL
SELECT 'burg' UNION ALL
SELECT 'berg') AS B(Suffix)
) Q
ORDER BY NEWID()
;
INSERT INTO #Users(Name)
SELECT Name + ' ' + Surname FROM (
SELECT Name, Surname FROM
(SELECT 'John' UNION ALL
SELECT 'Mary' UNION ALL
SELECT 'Ann' UNION ALL
SELECT 'Salomon' UNION ALL
SELECT 'Lisa' UNION ALL
SELECT 'Patricia' UNION ALL
SELECT 'David' UNION ALL
SELECT 'Patrick' UNION ALL
SELECT 'John' UNION ALL
SELECT 'Harry' UNION ALL
SELECT 'Richard' UNION ALL
SELECT 'George'
) as A(Name)
CROSS JOIN
(SELECT 'Smith' UNION ALL
SELECT 'Kowalski' UNION ALL
SELECT 'Bush' UNION ALL
SELECT 'Truman' UNION ALL
SELECT 'Clinton' UNION ALL
SELECT 'Reagan' UNION ALL
SELECT 'Lincoln' UNION ALL
SELECT 'Goldberg' UNION ALL
SELECT 'Adams' UNION ALL
SELECT 'Wilson' UNION ALL
SELECT 'Carter') as B(Surname)
) P
ORDER BY NEWID();
INSERT INTO #BeenHere(UserID, TownID)
SELECT
TOP 10 PERCENT
#Users.ID,
#Towns.ID
FROM
#Users
CROSS JOIN
#Towns
ORDER BY NEWID();
SET NOCOUNT OFF;
SELECT
Towns.Name,
(SELECT Users.ID, Users.Name FROM #Users Users INNER JOIN #BeenHere BH ON Users.ID = BH.UserID WHERE BH.TownID = Towns.ID ORDER BY Users.Name FOR XML PATH('User'), ROOT('Users'), TYPE) as BeenThere
FROM #Towns Towns
ORDER BY Towns.Name;
DROP TABLE #BeenHere;
DROP TABLE #Users;
DROP TABLE #Towns;
As we can see in execution plan, sorting users cost 78% of resources consumed by last query.
Is it possible to place some index on these tables to improve sorting perfomance? I can't introduce backward incompatible changes to database, like providing clustered index on #BeenHere(UserID, TownID).
I simply replaced your clustered index into this one:
CREATE CLUSTERED INDEX IDX_C_Users_Name_UserID ON #Users(Name, ID);
So now your table is sorted by Name, not by ID.
SORT operator is gone from execution plan.
UPDATE
As you said, you cannot change clustered index. There's one way to do that if you want. Your NONCLUSTERED INDEX on Name only column is fine, but SQL Server decides not to use it. What you can do is to add a HINT to your table to use this index:
SELECT Towns.Name
, (
SELECT Users.ID, Users.Name
FROM #Users Users WITH (INDEX (IDX_Users_Name))
INNER JOIN #BeenHere BH
ON Users.ID = BH.UserID
WHERE BH.TownID = Towns.ID
ORDER BY Users.Name
FOR XML PATH('User'), ROOT('Users'), TYPE
) AS BeenThere
FROM #Towns Towns
ORDER BY Towns.Name;
Then your query will use this index and sort operator will no longer be there. However, I'm not sure if it's the most efficient way. SQL Server has to Scan index then, instead of seeking it.
Your problem is the use of a correlated subquery. Stop using those and use joins (including derived tables if you must) instead. Correlated subqueries run row-by-row and not against the whole set and thus are preformance hogs. one.

Sql server union but keep order

Is there a way to union two tables, but keep the rows from the first table appearing first in the result set?
For example:
Table1
name surname
-------------------
John Doe
Bob Marley
Ras Tafari
Table2
name surname
------------------
Lucky Dube
Abby Arnold
I want the result set to look like:
name surname
-------------------
John Doe
Bob Marley
Ras Tafari
Lucky Dube
Abby Arnold
Unfortunately, union somehow reorders the table. Is there a way around this?
Try this :-
Select *
from
(
Select name,surname, 1 as filter
from Table1
Union all
Select name,surname , 2 as filter
from Table2
)
order by filter
The only way to guarantee output order is to use ORDER BY:
SELECT name,surname,1 as rs
FROM table1
UNION ALL
SELECT name,surname,2
FROM table2
ORDER BY rs
If you don't want rs to appear in the final result set, do the UNION as a subquery:
SELECT name,surname
FROM (
SELECT name,surname,1 as rs
FROM table1
UNION ALL
SELECT name,surname,2
FROM table2
) t
ORDER BY rs
;WITH cte as (
SELECT name, surname, 1 as n FROM table1
UNION ALL
SELECT name, surname, 2 as n FROM table2
UNION ALL
SELECT name, surname, 3 as n FROM table3
)
SELECT name, surname
FROM cte
ORDER BY n;
.Like this?
CREATE TABLE #Table1 (Names VARCHAR(50))
CREATE TABLE #Table2 (Names VARCHAR(50))
INSERT INTO #Table1
(
Names
)
VALUES
('John Doe'), ('Bob Marley'), ('Ras Tafari')
INSERT INTO #Table2
(
Names
)
VALUES
('Lucky Dube'), ('Abby Arnold')
SELECT ArbSeq = 1, *
FROM #Table1
UNION ALL
SELECT ArbSeq = 2, *
FROM #Table2
ORDER BY ArbSeq
It should be noted that ordering is not guaranteed when not explicitly defined.
If the table features a clustered index, the rows will typically be returned in the index's order - but this is not guaranteed.

In mySQL, Is it possible to SELECT from two tables and merge the columns?

If I have two tables in mysql that have similar columns...
TABLEA
id
name
somefield1
TABLEB
id
name
somefield1
somefield2
How do I structure a SELECT statement so that I can SELECT from both tables simultaneously, and have the result sets merged for the columns that are the same?
So for example, I am hoping to do something like...
SELECT name, somefield1 FROM TABLEA, TABLEB WHERE name="mooseburgers";
...and have the name, and somefield1 columns from both tables merged together in the result set.
Thank-you for your help!
Sample output appended because question unclear:
I want the rows from table1 and the rows from table2 appended in the resultset. For example if the tables contain
TABLEA
id(1) name(zoot) somefield(suit)
TABLEB
id(1) name(zoot) somefield(flute)
The resultet would look like:
name | somefield1
zoot suit
zoot flute
You can combine columns from both tables using (id,name) as the joining criteria with:
select
a.id as id,
a.name as name,
a.somefield1 || ' ' || b.somefied1 as somefield1
from tablea a, tableb b
where a.id = b.id
and a.name = b.name
and b.name = 'mooseburgers';
If you want to join on just the (id) and combine the name and somefield1 columns:
select
a.id as id,
a.name || ' ' || b.name as name,
a.somefield1 || ' ' || b.somefied1 as somefield1
from tablea a, tableb b
where a.id = b.id
and b.name = 'mooseburgers';
Although I have to admit this is a rather unusual way of doing things. I assume you have your reasons however :-)
If I've misunderstood your question and you just want a more conventional union of the two tables, use something like:
select id, name, somefield1, '' as somefield2 from tablea where name = 'mooseburgers'
union all
select id, name, somefield1, somefield2 from tableb where name = 'mooseburgers'
This won't combine rows but will instead just append the rows from the two queries. Use union on its own if you want to remove duplicate rows but, if you're certain there are no duplicates or you don't want them removed, union all is often more efficient.
Based on your edit, the actual query would be:
select name, somefield1 from tablea where name = 'zoot'
union all
select name, somefield1 from tableb where name = 'zoot'
(or union if you don't want duplicates where a.name==b.name=='zoot' and a.somefield1==b.somefield1).
I am not sure what you mean by merge, but you can UNION the results:
SELECT id, name, somefield1 FROM TABLEA WHERE name="mooseburgers"
union all
SELECT id, name, somefield1 FROM TABLEB WHERE name="mooseburgers";
Depending on what you mean by merge, here's a possible solution.
Select id,name,somefield1 from TableA
WHERE Name ='mooseburgers'
UNION
Select id,name,somefield1 from TableB
WHERE Name ='mooseburgers'
Will allow you to show results from both tables with the results merged into 1 table.
Do you perhaps mean
SELECT tableA.id, tableA.name, tableA.somefield1
FROM tableA, tableB
WHERE tableA.name = tableB.name AND tableA.name="mooseburgers"

Resources