Building a SQL query that doesn't include data based on hierarchy - sql

I've spent quite a bit of time on Google and SO the last few days, but I can't seem to find an answer to my problem. Not knowing exactly how to phrase the issue into a reasonable question makes it a little more difficult. You don't know what you don't know, right?
Due to business limitations I can't post my exact code and database structure, so I'll do my best to give a solid example.
Customer Table - Holds customer data
[CustId] | [CustName]
---------------------
1 | John Smith
2 | Jane Doe
3 | John Doe
Code Table - Holds code data
[CodeId] | [CodeDesc]
---------------------
A | A Desc
B | B Desc
C | C Desc
D | D Desc
E | E Desc
CustomerCode Table - Combines customers with codes
[CustId] | [CodeId]
-------------------
1 | A
1 | B
2 | B
2 | C
2 | D
3 | C
3 | E
CodeHierarchy Table - Hierarchy of codes that shouldn't be included (DropCode) if a customer has a ConditionCode
[ConditionCode] | [DropCode]
----------------------------
A | B
B | C
B | D
Now I'll try to explain my actual question.
What I'm trying to accomplish is writing a query (view) that will list codes based on the CodeHierarchy table.
Results would be something like this:
[CustName] | [CodeId]
-------------------
John Smith | A
Jane Doe | B
John Doe | C
John Doe | E
Code B isn't listed for John Smith since he has code A. Codes C and D aren't listed for Jane Doe since she also has code B. John Doe has all codes listed (notice that E isn't even in the CodeHierarchy table).
I've tried a few different things (inner joins, left/right joins, subqueries, etc) but I just can't get the results I'm looking.
As a base query, this returns all codes:
SELECT
Customer.CustomerName,
Code.CodeDesc
FROM
Customer
INNER JOIN CustomerCode
ON Customer.CodeId = CustomerCode.CodeId
INNER JOIN Code
ON CustomerCode.CodeId = Code.CodeId
This only returns codes that are ConditionCodes (I understand why, but I though it might be worth a shot at the time):
SELECT
Customer.CustomerName,
Code.CodeDesc
FROM
Customer
INNER JOIN CustomerCode
ON Customer.CodeId = CustomerCode.CodeId
INNER JOIN Code
ON CustomerCode.CodeId = Code.CodeId
INNER JOIN CodeHierarchy
ON Customer.CodeId = CodeHierarchy.ConditionCode
AND Customer.CodeId != CodeHierarchy.DropCode
I tried a subquery (don't have that code available) that ended up dropping all DropCodes, regardless if a member did or did not have a qualifying hierarchy (i.e. customer rows with B weren't returned even if they didn't have A)
I had an idea of making the base query above a subquery and the joining it with the CodeHierarchy table, but I'm stuck on how to write the query:
SELECT
*
FROM
(
base query (with all codes)
) CustomerCodesAll
INNER/LEFT JOIN CodeHierarchy
ON ?
I've also been doing some reading on CTEs, but I'm not sure how I could make use of that technique.
This will end up being a view to be queried against for reporting purposes. The customer table contains much more data including dob, gender, company status, etc. The view will be straightforward and pull everything. Queries against the view will include where clauses for dob, gender, etc.
Can anyone point me in the right direction?
Thanks for any help.

SELECT
Customer.CustName,
Code.CodeDesc
FROM
Customer
INNER JOIN CustomerCode AS posCustomerCode
ON Customer.CustId = posCustomerCode.CustId
INNER JOIN Code
ON posCustomerCode.CodeId = Code.CodeId
LEFT JOIN CodeHierarchy
ON posCustomerCode.CodeId = CodeHierarchy.DropCode
WHERE
CodeHierarchy.ConditionCode NOT IN (
SELECT CodeId
FROM CustomerCode AS negCustomerCode
WHERE negCustomerCode.CustId=posCustomerCode.CustId
)
OR CodeHierarchy.ConditionCode IS NULL
SQLfiddle

Related

SQLite: Splitting multiple selects into different columns

I need to achieve the following looking output in SQL:
chair | Secretary | Year
------------------------
Matt | Susan | 2006
Susan | Joe | 2005
From a database with tables Members and Leaders. Table Members looks as follows:
Name | num
-------------
Matt | 123
Susan | 456
Joe | 789
Table Leaders looks as follows:
Year | Chair | Secretary
-------------------
2006 | 123 | 456
2005 | 456 | 789
So far I've come up with something like this:
SELECT * FROM
(SELECT m.name FROM Members M, Leaders L
WHERE M.num = L.secretary
UNION ALL
SELECT m.name FROM MEMBER M, Leaders L WHERE M.num = L.chair
UNION ALL
SELECT L.year from Leaders L);
However, this selects all of the wanted parameters as one column. My question is: How do I make it so that the names in particular are split into Chair and Secretary columns when they are derived from the same table?
In my opinion you could build your query in this way:
SELECT l.year, m1.Name, m2.Name
FROM leaders l
INNER JOIN members m1 ON l.chair = m1.num
INNER JOIN members m2 ON l.secretary = m2.num
You can find a test here
You have to use ALIAS, which can be defined with AS keyword (that can be omitted). ALIAS can be used for tables and for columns, like in the example below:
SELECT
ChairMembers.Name AS Chair,
SecretaryMembers.Name AS Secretary,
Year
FROM
Leaders
INNER JOIN
Members AS ChairMembers
ON
Leaders.Chair = ChairMembers.num
INNER JOIN
Members AS SecretaryMembers
ON
Leaders.Secretary = SecretaryMembers.num
For more information you can read this documentation.

combine 2 sql's from different tables into one query

I have for example as first query: (ararnr = article number)
Select ararnr,ararir,aoarom from ar left join ao ON AR.ARARNR=AO.AOARNR WHERE AR.ARARKD=1389
the second query uses the result from the first column from the first query to search in another table
Select votgan, sum(ststan) as totalStock from vo INNER JOIN st on vo.voarnr=st.starnr where voarnr = ararnr
How could I combine both ?
Please note : Not all articlenumbers from the first query will be found in the second, but I need them in my result.
In the result I need the columns from both queries.
EDIT
for example :
first query returns article numbers and the description:
+---------+--------------+
| ararnr | aoarom |
+---------+--------------+
| a123456 | description1 |
| b123456 | description2 |
| 0123456 | description3 |
+---------+--------------+
second query returns the totalstock for those articles:
+---------+--------------+
| ararnr | totalstock |
+---------+--------------+
| a123456 | 12 |
| b123456 | |
| 0123456 | 6 |
+---------+--------------+
Note the second one doesn't return a value since the articlenumber doesn't exist in this table.
In my result I would like to get the articlenumber with corresponding description and stock.
+---------+--------------+-----------+---------+
| ararnr | aoarom | totalStock| vovoan |
+---------+--------------+-----------+---------+
| a123456 | description1 | 12 | 2 |
| b123456 | description2 | | 1 |
| 0123456 | description3 | 6 | |
+---------+--------------+-----------+---------+
I'm using sql on db2
SECOND EDIT
The first query will select some article numbers (ararnr) from table ar and find the corresponding description (aoarom) in another table ao.
The second query finds the stock (vovoan and sum ststan) from two differend tables vo and st for the article numbers found in the first query.
The result should have the article number with corresponding description with corresponding stock from vo and st
I can't fully understand what you're asking, but another join may assist you.
example:
SELECT ar.ararnr, ar.ararir, ar.ararom, vo.votgan, SUM(vo.ststan) as totalStock
FROM ar LEFT JOIN ao ON [id=id] LEFT JOIN vo ON [id=id]
Because I can't tell what your tables structure are, or what you're really asking for, this is the best response I can give you.
This also may be what you're looking for:
Combining 2 SQL queries and getting result set in one
You can use this query.
SELECT ar.ararnr, ar.ararir, ar.ararom, vo.votgan, SUM(vo.ststan) as totalStock
FROM ar
LEFT JOIN ao ON ao.ararnr = ar.ararnr
LEFT JOIN vo ON vo.voarnr = ao.ararnr
If you are using SQL Server as database then this can be done with help of OUTER APPLY
SELECT ararnr,aoarom ,temp.totalStock
FROM ar
LEFT JOIN ao ON AR.ARARNR=AO.AOARNR
OUTER APPLY(
SELECT sum(ststan) as totalStock
FROM vo
INNER JOIN st on vo.voarnr=st.starnr
where voarnr = ar.ararnr
)temp
WHERE AR.ARARKD=1389
You'd get a much more complete answer if you were to post the table structure and desired result, but..
You can use the first query as a resultset for your second query, and join to it. something like:
Select
votgan,
sum(ststan) as totalStock
from vo
inner join (Select
ararnr,
ararir,
ararom
from ar
left join ao .....) z on vo.voarnr = z.ararnr
EDIT:
Select
votgan,
sum(ststan) as totalStock,
z.ararnr,
z.aoarom
from vo
inner join (Select
ararnr,
ararir,
ararom
from ar
left join ao .....) z on vo.voarnr = z.ararnr

one to many select in sql

Well i have two tables first for employees and the second for emails, i want to see the employees with all his emails
table employees:
id | name | idE
1 | name1 | 1
2 | name2 | 2
3 | name3 | 3
table email:
id | idE | email
1 | 1 | e1#email.com
2 | 1 | e2#email.com
3 | 2 | e#email2.com
4 | 3 | e#email3.com
and i have this query:
select e.id, e.name, m.email from employees as e inner join email as m on p.idE = m.idE
and this is the result:
id | name | email
1 | name1 | e1#email.com
1 | name1 | e2#email.com
2 | name2 | e#email2.com
3 | name3 | e#email3.com
i dont need employee 1 twice i just need both emails in the same row
Hi guys i modified my query to:
select em.name,
STUFF(
(select ',' + e.Email from tbl_Employees as m inner join tbl_Email as e on m.idE = e.idE for xml path(''))
,1,1,'') as emails
from tbl_employees as em
but now my problem is that i get this in each column of email:
e1#email.com,e2#email.com,e#email2.com,e#email3.com,
how can i do it to get only the emails of each employee?
instead of having every single email in each column
for example:
name | email
name1 | e1#email.com,e2#email.com,
name2 | e#email2.com,
name3 | e#email3.com,
pd: sorry if i have some mistakes im still learning english
Although what you're asking for is possible, and I'm sure you'll get several answers, your question is a good example of be careful what you wish for ! You're asking the DBMS to supply some presentation-level functionality. You're usually better off letting SQL find the data you need, and handling presentation issues in your application.
SELECT returns a table of information, and the value of each column in a row is normally "atomic" in the sense that it holds a single value that SQL can compare to other values. The tables in your database have the same property if they adhere to first normal form.
One of the virtues of SQL is that "everything is a table": you can stack SELECTs one atop the other.
select * from (select a, b from T where a > b) as Z
Each inner one produces another table that the next outer one can act on.
Functions like group_concat (as an example) produce a non-atomic column. It's only a short step from there to code like:
select * from ( select group_concat(a) as a_list from T ) as Z
where a_list like '%Henry%'
The programmer is forced into search strings instead of direct comparison. The query becomes less precise and thus error-prone.
The fail-safe approach, in your example, is to read the results one by one from the DBMS, and concatenate a per-user address list by watching the idE column. It will keep your query simpler, and if you later decide to present them another way, you can do that without diving into the database logic.
this is my query after the changes
select em.name,
STUFF(
(select ',' + e.Email from tbl_Employees as m inner join
tbl_Email as e on m.idE = e.idE
where em.idE = e.idE
for xml path('')
) ,1,1,'') as emails
from tbl_employees as em
and the results of this query
name | emails
name1 | e1#email.com,e2#email.com
name2 | e#email2.com
name3 | e#email3.com
thank you for the answers!

How can I find all columns A whose subcategories B are all related to the same column C?

I'm trying to better understand relational algebra and am having trouble solving the following type of question:
Suppose there is a column A (Department), a column B (Employees) and a column C (Managers). How can I find all of the departments who only have one manager for all of their employees? An example is provided below:
Department | Employees | Managers
-------------+-------------+----------
A | John | Bob
A | Sue | Sam
B | Jim | Don
B | Alex | Don
C | Jason | Xie
C | Greg | Xie
In this table, the result I should get are all tuples containing departments B and C because all of their employees are managed by the same person (Don and Xie respectively). Department A however, would not be returned because it's employees have multiple managers.
Any help or pointers would be appreciated.
Such problems usually call for a self-join.
Joining the relation onto itself on Department, then filtering out the tuples where the Managers are equal would yield us all the unwanted tuples, which we can just subtract from the original relations.
Here's how I'd do it:
First we make a copy of table T, and call it T2, then take a cross product of T and T2. From the result we select all the rows where T1.Manager /= T2.Manager but T1.Department=T2.Department, yielding us these tuples:
T1.Department | T1.Employees| T1.Managers | T2.Managers | T2.Employees | T2.Department
--------------+-------------+-------------+-------------+--------------+--------------
A | John | Bob | Sam | Sue | A
A | Sue | Sam | Bob | John | A
Departments A and B aren't present because their T1.Manager always equals T2.Manager.
Then we just subtract this result the original set to get the answer.
If your RDBMS supports common table expressions:
with C as (
select department, manager, count(*) as cnt
from A
group by department, manager
),
B as (
select department, count(*) as cnt
from A group by department
)
select A.*
from A
join C on A.department = C.department
join B on A.department = B.department
where B.cnt = C.cnt;

SQL many-to-many select help needed

I have 2 tables
Bid_customer
|bidkey | customerkey
| 1 | 1
| 1 | 2
| 1 | 3
customer_groups
| groupkey | customerkey
| 1 | 1
| 1 | 2
| 1 | 3
What I'm trying to get is a result that will look like
| bidkey | groupkey
| 1 | 1
I've tried a cursor and joins but just don't seem to be able to get what i need any ideas or suggestions
EDIT: customers can belong to more that one group also
I am not sure who meaningful your sample data is. However following is a simple example.
Query:
select distinct b.bidkey, g.gkey
from bidcus b
inner join cusgroup g
on
b.cuskey = g.cuskey
and g.gkey = 10;
Results:
BIDKEY GKEY
1 10
Reference: SQLFIDDLE
In order to have a working Many-to-Many relationship in a database you need to have an intermediary table that defines the relationship so you do not get duplicates or mismatched values.
This select statement will join all bids with all groups because the customer matches.
Select bidkey, groupkey
From customer_groups
Inner Join bid_customer
Where customer_groups.customerkey = Bid_customer.customerkey
Hers is a sample Many to Many Relationship:
For your question:
You will need another table that joins the data. For example, GroupBids
customer_groups and bid_customer would have a one-to-many relationship with GroupBids
You would then do the following select to get your data.
Select bidkey, groupkey
From bid_customer
inner join GroupBids
ON bid_customer.primarykey = GroupBids.idBidKey
inner join customer_groups
ON customer_groups.primarykey = GroupBids.idCustomerGroupkey
This would make sure only related groups and bids are returned