SQL: Check if multiple records exist in multiple tables - sql

In my database, I have a table with a many-to-many relationship to several other tables. I'd like to know, for several records at a time, whether an item exists in each of the other tables. Here's a simple example diagram:
---------------
| base_table |
---------------
| key | name |
---------------
| 1 | item1 |
| 2 | item2 |
| 3 | item3 |
| 4 | item4 |
---------------
-------------------------------
| table_2 |
-------------------------------
| key | base_key | other_key |
-------------------------------
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 2 | 1 |
| 5 | 2 | 4 |
| 6 | 4 | 4 |
-------------------------------
-------------------------------
| table_3 |
-------------------------------
| key | base_key | other_key |
-------------------------------
| 1 | 2 | 1 |
| 2 | 3 | 2 |
-------------------------------
And then I'm looking for output like this:
-----------------------------------
| name | in_table_2 | in_table_3 |
-----------------------------------
| item1 | true | false |
| item2 | true | true |
| item3 | false | true |
| item4 | true | false |
-----------------------------------
I'm using MS SQL Server.

You can use union all and aggregation to get which keys are in which tables:
select base_key, max(in_2) as in_2, max(in_3) as in_3
from ((select distinct base_key, 1 as in_2, 0 as in_3
from table2
) union all
(select distinct base_key, 0 as in_2, 1 as in_3
from table3
)
) t
group by base_key;
This returns "1" if the key is in the table and "0" if it is not. SQL Server does not have boolean types and it seems silly to create a string for this purpose.
If you actually need the name instead of the key value, just join it in.

Though I liked #GordonLindoff's post, I thought the following would have worked just as well:
SELECT DISTINCT
b.Name,
in_table_2 = CASE WHEN c.Base_key IS NULL THEN 0 ELSE 1 END,
in_table_3 = CASE WHEN d.Base_key IS NULL THEN 0 ELSE 1 END
FROM Base_Table b
LEFT JOIN Table_2 c
ON b.key = c.Base_Key
LEFT JOIN Table_3 d
ON b.key = d.Base_Key;
I'd also reiterate his comment about 1's and 0's in SQL. If you really, really need "True" or "False" in your display, do it on the front end, or change the 0 and 1 in the case statements to False and True, respectively.
Anyone have any objections to this?
I did to a SQLFiddle, listed here. http://sqlfiddle.com/#!18/d6547/28

Related

SQL return only rows where value exists multiple times and other value is present

I have a table like this in MS SQL SERVER
+------+------+
| ID | Cust |
+------+------+
| 1 | A |
| 1 | A |
| 1 | B |
| 1 | B |
| 2 | A |
| 2 | A |
| 2 | A |
| 2 | B |
| 3 | A |
| 3 | B |
| 3 | B |
| 3 | C |
| 3 | C |
+------+------+
I don't know the values in column "Cust" and I want to return all rows where the value of "Cust" appears multiple times and where at least one of the "ID" values is "1".
Like this:
+------+------+
| ID | Cust |
+------+------+
| 1 | A |
| 1 | A |
| 1 | B |
| 1 | B |
| 2 | A |
| 2 | A |
| 2 | A |
| 2 | B |
| 3 | A |
| 3 | B |
| 3 | B |
+------+------+
Any ideas? I can't find it.
You may use COUNT window function as the following:
SELECT ID, Cust
FROM
(
SELECT ID, Cust,
COUNT(*) OVER (PARTITION BY Cust) cn,
COUNT(CASE WHEN ID=1 THEN 1 END) OVER (PARTITION BY Cust) cn2
FROM table_name
) T
WHERE cn>1 AND cn2>0
ORDER BY ID, Cust
COUNT(*) OVER (PARTITION BY Cust) to check if the value of "Cust" appears multiple times.
COUNT(CASE WHEN ID=1 THEN 1 END) OVER (PARTITION BY Cust) to check that at least one of the "ID" values is "1".
See a demo.

How to compare two rows of a table

I have a sample table with input as shown. In actual it has large amounts of data. The input and output as shown:
I/P- O/P-
|ID | Role | Status | |ID | Role | Status | Final |
|:-:|:----:|:------:| |:-:|:----:|:------:|:-----:|
| 1 | ABC | Pass | | 1 | ABC | Pass | 0 |
| 2 | PQR | Pass | | 2 | PQR | Pass | 0 |
| 1 | ABC | Fail | | 1 | ABC | Fail | 0 |
| 3 | PQR | Fail | | 3 | PQR | Fail | 0 |
| 1 | XYZ | Fail | | 1 | XYZ | Fail | 1 |
| 3 | PQR | Fail | | 3 | PQR | Fail | 1 |
| 3 | XYZ | Pass | | 3 | XYZ | Pass | 0 |
| 4 | XYZ | Fail | | 4 | XYZ | Fail | 1 |
So for a 'ID' (ex. ID=1) if Role is same for two or more records (ex. Role = 'ABC') then will status field. If for any of the selected record 'Status' = 'Pass' then will create a new field named Final with value 0 for all records(if for any of the record Status = 'Pass')
else will keep it 1(if there is no record with 'Status' = 'Pass'.
I know this we can do with Case statement while comparing columns. But not sure how to do with rows. Can someone please help me.
Thanks.
You can do an exists check. And tis coud be written in different ways depending on your backend which you didn't specify:
select Id, Role, Status,
case when exists (select * from myTable t2
where t1.Id = t2.Id
and t1.Role = t2.Role
and t2.Status = 'Pass') then 0 else 1 end as Final
from myTable t1;
DBFiddle demo
PS: Either your sample output is wrong or your explanation. You may need to change the where part depending on your actual need.

Match and set child's ID based on condition

I've got a primary incremental ID column and have to find and set all its childs (in ParentID column) based on values from two other columns (Condition1 and Condition2)
Started ParentID always has Condition2 = 1 (and the same value in Condition1 column)
Initial table
+---------------------------------------------
| ID | ParentID | Condition1 | Condition2 |
+---------------------------------------------
| 1 | null | 1000 | 1 |
| 2 | null | 1000 | null |
| 3 | null | 1000 | null |
| 4 | null | 2000 | 1 |
| 5 | null | 2000 | null |
| 6 | null | 2000 | null |
| 7 | null | 3000 | 1 |
| 8 | null | 3000 | null |
| 9 | null | 3000 | null |
+---------------------------------------------
Desired Output
+---------------------------------------------
| ID | ParentID | Condition1 | Condition2 |
+---------------------------------------------
| 1 | 1 | 1000 | 1 |
| 2 | 1 | 1000 | null |
| 3 | 1 | 1000 | null |
| 4 | 4 | 2000 | 1 |
| 5 | 4 | 2000 | null |
| 6 | 4 | 2000 | null |
| 7 | 7 | 3000 | 1 |
| 8 | 7 | 3000 | null |
| 9 | 7 | 3000 | null |
+---------------------------------------------
Current code returns only one row for each new ID
update u
set u.ParentID = u.ID
from [db].[dbo].[tbl] u
inner join [db].[dbo].[tbl] on
u.Condition2 = 1 and u.Condition1 = u.Condition1
I think one intuitive way of doing this is
UPDATE [db].[dbo].[tbl] u
SET u.ParentID = (SELECT id FROM [db].[dbo].[tbl] u2 WHERE u2.condition1 = u.condition1 and u2.condition2 = 1)
I don't mean that this is better than what you're trying to do, just that I think it's very intuitive and easy to understand if you're having issues.
I think the solution you are looking for is:
update u
set u.ParentID = u2.ID
from [db].[dbo].[tbl] u
inner join [db].[dbo].[tbl] u2 on
u2.Condition2 = 1 and u.Condition1 = u2.Condition1
The important differences here are that I have given the both tables in the join-to-self a name (u and u2). You don't want to set u.ParentID = u.ID (as in your question), you want to set u.ParentID = u2.ID (note the 2). Similarly, you don't want to join the tables on u.condition1 = u.condition1 (since that is always true in this example) or u.condition2 = 1 (since you're applying that condition to the table you're updating, not the table you joined). Even though it is a join-to-self, you need to be clear about which table you are referencing. u in your query refers to the table being updated, but not the table on the right-side of the join.

Create a combined list from two tables

I have a table with CostCenter_ID (int) and a second table with Process_ID (int).
I'd like to combine the results of both tables so that each cost center ID is assigned to all process IDs, like so:
|CostCenterID | ProcessID |
---------------------------
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
I've done it before but I'm drawing a blank. I've tried this:
SELECT CostCenter_ID,NULL FROM dbo.Cost_Centers
UNION ALL
SELECT NULL,Process_ID FROM dbo.Processes
which returns this:
|CostCenterID | ProcessID |
---------------------------
| 1 | NULL |
| NULL | 1 |
| NULL | 2 |
| NULL | 3 |
Try:
select a.CostCenterID, b.ProcessID
from table1 a
cross join table2 b
or:
select a.CostCenterID, b.ProcessID
from table1 a
,table2 b
NB: cross join is the better method as it makes it clearer to the reader what your intentions are.
More info (with pics) here: http://www.w3resource.com/sql/joins/cross-join.php

Query for data in two tables connected by a third. Data Sometimes only on one

I thought I could figure this out but I am having a lot of issues.I have 3 Tables, Table1, Table2, and Table3. These tables where designed by someone else and I have to work with them. They were not designed to be used the way they are used today.
The bottom line is I need to be able to enter an Item_No, this will always exist in Table2. And if the Item_No can also be found in Table 3, could be multiple times or none, and there can be times where I can find it 5 times in Table2 and only 3 times in Table3. If it is in Table3 it will also be in Table1.
So, using the Item_No i can find on Table2, return the Order_qty's associated with those rows. Then using the if exist getting Table1.ID where Table1.ID = Table3.ID WHERE Table3.Item_No = Table2.Item_No
I came up with the following, it does not give me errors but simply stops code execution during a C# fill. I had it working for finding the Item_No on Table3 and returning what it finds, I have ONLY changed this line of code since so I KNOW this is the issue.
Here is what I could come up with that is not working:
SELECT Table1.ID,
Table2.Order_Qty As [Qty of Full Order], Table2.Item_No As [Set No]
FROM Table2
LEFT JOIN Table3
ON Table2.Item_No = Table3.Item_No
AND Table2.Order_No = Table3.Order_No
LEFT JOIN Table1
ON Table1.Order_No = Table2.Order_No
AND Table1.ID = Table3.ID
WHERE Table2.Item_No = #m_strUserEnteredSeachValue
ORDER BY Table2.Order_No DESC
*Example Data: *
Table 1
+----------+--------------+-------------------+
| Order_No | Sub_Order_No | Sub_Order_Contact |
+==========+==============+===================+
| 1 | 1 | John Doe |
+----------+--------------+-------------------+
| 1 | 2 | Jane Doe |
+----------+--------------+-------------------+
| 1 | 3 | Foo |
+----------+--------------+-------------------+
| 1 | 4 | Bar |
+----------+--------------+-------------------+
| 1 | 5 | Foo2 |
+----------+--------------+-------------------+
Table 2
+----------+--------------+-------------------+
| Order_No | Item_No | Customer_Item_Name|
+==========+==============+===================+
| 1 | 1 | 1234567890 |
+----------+--------------+-------------------+
| 1 | 2 | 1234567891 |
+----------+--------------+-------------------+
| 1 | 3 | 1234567892 |
+----------+--------------+-------------------+
| 1 | 4 | 1234567893 |
+----------+--------------+-------------------+
| 1 | 5 | 1234567894 |
+----------+--------------+-------------------+
| 1 | 6 | 1234567895 |
+----------+--------------+-------------------+
| 2 | 1 | 0987654321 |
+----------+--------------+-------------------+
| 2 | 2 | 0987654322 |
+----------+--------------+-------------------+
| 2 | 3 | 0987654323 |
+----------+--------------+-------------------+
| 3 | 1 | 1234567893 |
+----------+--------------+-------------------+
And Table 3
+----------+--------------+-------------------+--------------+
| Order_No | Item_No | Customer_Item_Name| Sub_Order_No |
+==========+==============+===================+==============+
| 1 | 1 | 1234567890 | 1 |
+----------+--------------+-------------------+--------------+
| 1 | 2 | 1234567891 | 2 |
+----------+--------------+-------------------+--------------+
| 1 | 3 | 1234567892 | 2 |
+----------+--------------+-------------------+--------------+
| 1 | 4 | 1234567893 | 3 |
+----------+--------------+-------------------+--------------+
| 1 | 5 | 1234567894 | 4 |
+----------+--------------+-------------------+--------------+
| 1 | 6 | 1234567895 | 4 |
+----------+--------------+-------------------+--------------+
| 1 | 4 | 1234567893 | 4 |
+----------+--------------+-------------------+--------------+
The Result I am looking for: If I search for Item 1234567893
+----------+--------------+-------------------+--------------+-------------------+
| Order_No | Item_No | Customer_Item_Name| Sub_Order_No | Sub_Order_Contact |
+==========+==============+===================+==============+===================+
| 3 | 1 | 1234567893 | | |
+----------+--------------+-------------------+--------------+-------------------+
| 1 | 4 | 1234567893 | 3 | Foo |
+----------+--------------+-------------------+--------------+-------------------+
| 1 | 4 | 1234567893 | 4 | Bar |
+----------+--------------+-------------------+--------------+-------------------+
A pragmatic answer to a problem like this is to split it into a couple of queries. Query Table #2 first, and then based on that result set, run additional queries into #1 or #3.
Another angle is to query on Table #2 and use subqueries to reach-out-there into Table #1 or Table #3 to fetch data you need.
Try this:
declare #m_strUserEnteredSeachValue varchar(10) = '1234567893';
with a as
(
select
Order_No, Item_No, Customer_Item_Name
from
Table2
UNION
select
Order_No, Item_No, Customer_Item_Name
from
Table3
)
select
a.Order_No,
a.Item_No,
a.Customer_Item_Name,
Table3.Sub_Order_No,
Table1.Sub_Order_Contact
from
a
left join
Table3
on
Table3.Order_No=a.Order_No
and Table3.Item_No=a.Item_No
and Table3.Customer_Item_Name=a.Customer_Item_Name
left join
Table1
on
Table1.Sub_Order_No = Table3.Sub_Order_No
where
#m_strUserEnteredSeachValue = a.Customer_Item_Name
order by
a.Item_No, Table3.Sub_Order_No
SqlFiddle demo: http://www.sqlfiddle.com/#!3/973d8/3
I have no idea if this is what you are trying to arrive at or not, since it's difficult to understand from you question. All I know that this query gives the dataset that you put in OP.