SELECT To obtain results from two tables where a value occurs once only in a table column - sql

Sorry I know this should be relatively simple and probably has been answered before, but I've got myself blocked on this and can't find a post here that matches.
I have two tables companies and contacts. A contact can be linked to 0 or 1 company, a company can have 1 or more contacts and MAY have one set as a 'primary contact'
companies(ccyID,ccyname,primconID)
________________________________________________
| ccyID | ccyname | PrimconID |
------------------------------------------------
| aaaaaaa | Company A | NULL |
| bbbbbbb | Company B | NULL |
| ccccccc | Company C | vvvvvvv |
________________________________________________
contacts(conID,firstname,lastname,ccyID)
__________________________________________________
| conID | first | last | companyID |
--------------------------------------------------
| zzzzzzz | Stand | Alone | NULL |
| yyyyyyy | Only | Contact | aaaaaaa |
| xxxxxxx | CompanyB | First | bbbbbbb |
| wwwwwww | CompanyB | Second | bbbbbbb |
| vvvvvvv | CompanyC | Only | ccccccc |
_________________________________________________
I need a SELECT that will return the companyID and contactID when the company has exactly one contact, AND does not have a PrimconID set i.e. for the above data I want returned
conID ccyID
----------------
yyyyyyy aaaaaaa
(The eventual idea is that I'm then going to update the tables to make the solitary contact for companies the primary contact)

Group contacts by company to keep only those with a count of 1 and make sure they are in the set of companies without primary contact:
select companyid, max(conid)
from contacts
group by companyid
having count(*) = 1
and companyid in (select ccyid from companies where primconid is null)
order by companyid;

I would use a join and group by:
select c.ccyid, max(co.conid)
from companies c join
contacts co
on co.companyid = c.ccyid
where c.PrimconID is null
group by c.ccyid
having min(co.conID) = max(co.conID);
As an update statement, you can do:
update c
set PrimconID = co.conid
from companies c join
(select co.companyid, max(co.conid) as conid
from contacts co
group by companyid
having min(co.conid) = max(co.conid)
) co
on co.companyid = c.ccyid
where c.PrimconID is null ;

Use a CTE to first identify all contacts having only a single company, and then join to the company table:
WITH cte AS (
SELECT ccyID, MAX(conID) AS conID
FROM contacts
GROUP BY ccyID
HAVING COUNT(*) = 1
)
SELECT
t.conID,
c.ccyID
FROM companies c
INNER JOIN cte t
ON c.ccyID = t.ccyID
WHERE
c.PrimconID IS NULL;
See the demo below to see the query working.
Demo

Related

How to update and delete duplicate in SQL Server?

I have two table Add (Parent table) and AddXref (Child Table) and want to delete duplicate records in parent table and also update in Child table. In AddXref table the two records should have anyone of AddId from Add Table.i Tried with basic update statement but it didn't worked. How can I update AddXref Table?
My Attempt:
I have written the query up to this point, but am stuck on what to do next
Update AddXref name
SET a.AddId=b.AddId
FROM AddXref a
INNER JOIN Add b ON a.AddId = b.AddId
WHERE b.AddId = ( Select Top 1 ax.Addid from Add ax
INNER JOIN Add ax1 on
ax.AddressMain = a.AddressMain and
ax.city = a.city and
ax.Country = a.Country and
ax.State = a.State and
ax.ZipCode = a.ZipCode);
| AddId | AddressMain | City | Country | State | ZipCode |
|--------|--------------|---------|---------|---------|------------|
| 8CA25D | 1234 Main St | Antioch | USA | Florida | 60002-9714 |
| 5T7YTR | 1234 Main St | Antioch | USA | Florida | 60002-9714 |
| AddXrefId | AddressId | IndividualId | InstitutionId |
|-----------|-----------|--------------|---------------|
| 822145 | 8CA25D | 7652ER | NULL |
| 435902 | 5T7YTR | NULL | NA1043 |
| AddXrefId | AddressId | IndividualId | InstitutionId |
|-----------|-----------|--------------|---------------|
| 822145 | 8CA25D | 7652ER | NULL |
| 435902 | 8CA25D | NULL | NA1043 | --Output either "8CA25D" or "5T7YTR" any one of it
first gather duplicate data.
select AddressMain, City, Country, State, ZipCode, min(addId) as minID, Max(addId) as MAxID
INTO #tmpData
from add
group by AddressMain, City, Country, State, ZipCode
second update child table
Update AddXref
SET a.AddId=b.MaxID
FROM AddXref a
INNER JOIN #tmpData b ON a.AddId = b.MinID -- you can use maxId too if you want.
third remove duplicate record from parents.
DELETE FROM Add where AddId IN (Select MinID FROM #tmpData)
This code is not tested.

SQL query to get table rows whose columns should not match with other table columns

Thanks in advance,
Actually I have two tables carts and checks. Carts table contains rows as below
id |username |orderid | exam_name | price
1 | Rajesh | ABC123 | PMP | $60
2 | Rajesh | ABC123 | CPM | $70
3 | David | ABC789 | ITIL | $80
checks table contains rows as below
id |username |order_id | exam | price
1 Rajesh | ABC123 | PMP | $60
2 Rajesh | ABC123 | CPM | $70
I need a row data of carts table whose orderid column and exam_name column should not match with checks table order_id column and exam column
Something like this as below:
id |username |orderid | exam_name | price
1 | David | ABC789 | ITIL | $80
One method is not exists:
select c.*
from carts c
where not exists (select 1
from checks ch
where ch.orderid = c.orderid and ch.exam_name = c.exam_name
);
A similar method is left join:
select c.*
from carts c left join
checks ch
on ch.orderid = c.orderid and ch.exam_name = c.exam_name
where ch.orderid is null;
And in some databases you can use not in:
select c.*
from carts c
where (c.orderid, c.exam_name) not in (select ch.orderid, ch.exam_name from checks ch);
select c.*
from carts c
left join checks ch on c.id = ch.id
where ch.id is null;
Hope this should solve your problem.
SELECT *
from Carts
where exam_name not in (select exam from checks)
and id not in (select id from checks)
SELECT * FROM Carts
WHERE NOT EXISTS (SELECT 1 FROM checks
WHERE checks.OrderId = Carts.OrderId
and Carts.exam_name = checks.exam_name)

Inner queries on the same table - is there a better way?

I have these two tables (simplified versions)
Orders
owner_id | user_1 | user_2 | amount | order_id
-----------------------------------------------
1 | 2 | 3 | 100 | AAA
1 | 7 | 2 | 200 | BBB
2 | 3 | 5 | 400 | CCC
Users
user_id | username
------------------
1 | John
2 | Robert
3 | Sally
4 | Mario
5 | Albert
6 | Hernest
7 | Homer
I need to get, in one query, all the info related to a particular order, including the owner_id, user_1, user_2 usernames.
The result I'm trying to achieve is the following:
owner_id | owner_username | user_1_id | user_1_username | user_2_id | user_2_username | order_total
----------------------------------------------------------------------------------------------------
1 | John | 2 | Robert | 3 | Sally | 100
So far I'm getting all I need with a query like this:
SELECT o.owner_id AS owner_id, (SELECT username FROM Users where user_id = 1) AS owner_username,
o.user_1 AS user_1_id, (SELECT username FROM Users where user_id = 2) AS user_1_username,
o.user_2 AS user_2_id, (SELECT username FROM Users where user_id = 3) AS user_2_username,
o.amount AS order_total
FROM Orders.o
WHERE o.order_id = 'AAA'
This is an example to retrieve the info for the first order.
I'm not very satisfied by the inner queries I have to do to get each username, I think it's kinda ugly.
Is there a more elegant or more performant way to get the usernames?
Thank you
This may help
SELECT od.*,
U1.username AS 'User_1_Name',
U2.username AS 'User_2_Name',
U3.username AS 'User_3_Name'
FROM Orders od
LEFT OUTER JOIN Users U1
ON od.Owner_Id = U1.User_Id
LEFT OUTER JOIN Users U2
ON od.User_1 = U2.User_Id
LEFT OUTER JOIN Users U3
ON od.User_2 = U3.User_Id
WHERE order_id = 'AAA'

How to iterate on subgroups in SQL Server 2008?

I have one table looking like
Customer:
| CUSTOMER_ID | CUSTOMER_NAME | BANK_ID |
-----------------------------------------
| 1 | a | b |
| 2 | b1 | c |
| 3 | b1 | d |
| 4 | C | e |
| 5 | a | f |
| 6 | b1 | g |
I have a query that looks for all customer names that are not unique and group them together. It also assigns a row number to the rows in each group.
The output of this query is:
RowNumber|customer_id | customer_name |
1 | 1 | a |
2 | 5 | a |
1 | 2 | b1 |
2 | 3 | b1 |
3 | 6 | b1 |
I want to iterate on all the groups. For each group I want to join the members of the group with rows in a different table.Is there any way to operate on each sub group and apply business logic on the items in each sub group ?
for example: let's assume that for each group I want to leave the first customer if all the customers in this group live in the same place and work at the same place.
I have the following table:
|customer id | address | workplace-name |
|1 | street1 | work1|
|2 | street2 | work1|
|3 | street1 | work2|
|4 | street5 | work7|
|5 | street1 | work1|
|6 | street2 | work1|
You can notice that only the customers in the first group live and work at the same place (customers id: 1,5). If you look at the second group (customers id:2,3,6) - they don't all live and work at the same place.
The result of this query will be: customer id 5 as it's in the same group with customer id 5 and they both live and work in the same place. But customer 5 is the second in this group.
What's the easiest way to do it ?
Try this:
WITH A(Customer_id, Customer_name)
AS(SELECT Customer_id, Customer_name
FROM Customer
WHERE Customer_name IN
(SELECT Customer_name FROM Customer
GROUP BY Customer_name
HAVING COUNT(Customer_name) >1)
)
SELECT RANK() OVER (ORDER BY Customer_id ASC) AS RowNumber
, Customer_id, Customer_Name
FROM A
ORDER BY Customer_name, Customer_id;
Or you can also use JOIN for that
WITH A(Customer_id, Customer_name)
AS (SELECT c.Customer_id, c.Customer_name
FROM Customer c
JOIN
(SELECT Customer_id FROM Customer
WHERE Customer_name IN ( SELECT Customer_name FROM Customer
GROUP BY Customer_name
HAVING COUNT(Customer_name) >1)
) AS c1
ON c.Customer_id = C1.customer_id)
SELECT RANK() OVER (ORDER BY A.customer_id ASC) AS RowNumber
, Customer_id, Customer_Name
FROM A
ORDER BY Customer_name, Customer_id;
See this SQLFiddle
I couldn't find any way to do it in a single query. I've created a cursor that does what I want to do: Iterate on all the rows of one table, apply business logic and insert relevant rows to the output table.

SQL Grouping with multiple joins combining results incorrectly

Hi I'm having trouble with my query combining records when it shouldn't.
I have two tables Authors and Publications, they are related by Publication ID in a many to many relationship. As each author can have many publications and each publication has many Authors. I want my query to return every publication for a set of authors and include the ID of each of the other authors that have contributed to the publication grouped into one field. (I am working with mySQL)
I have tried to picture it graphically below
Table: authors Table:publications
AuthorID | PublicationID PublicationID | PublicationName
1 | 123 123 | A
1 | 456 456 | B
2 | 123 789 | C
2 | 789
3 | 123
3 | 456
I want my result set to be the following
AuthorID | PublicationID | PublicationName | AllAuthors
1 | 123 | A | 1,2,3
1 | 456 | B | 1,3
2 | 123 | A | 1,2,3
2 | 789 | C | 2
3 | 123 | A | 1,2,3
3 | 456 | B | 1,3
This is my query
Select Author1.AuthorID,
Publications.PublicationID,
Publications.PubName,
GROUP_CONCAT(TRIM(Author2.AuthorID)ORDER BY Author2.AuthorID ASC)AS 'AuthorsAll'
FROM Authors AS Author1
LEFT JOIN Authors AS Author2
ON Author1.PublicationID = Author2.PublicationID
INNER JOIN Publications
ON Author1.PublicationID = Publications.PublicationID
WHERE Author1.AuthorID ="1" OR Author1.AuthorID ="2" OR Author1.AuthorID ="3"
GROUP BY Author2.PublicationID
But it returns the following instead
AuthorID | PublicationID | PublicationName | AllAuthors
1 | 123 | A | 1,1,1,2,2,2,3,3,3
1 | 456 | B | 1,1,3,3
2 | 789 | C | 2
It does deliver the desired output when there is only one AuhorID in the where statement.
I have not been able to figure it out, does anyone know where i'm going wrong?
To eliminate duplicate authors, change:
ON Author1.PublicationID = Author2.PublicationID
to:
ON Author1.PublicationID = Author2.PublicationID AND
Author1.AuthorID <> Author2.AuthorID
Also, change:
GROUP BY Author2.PublicationID
to:
GROUP BY Author1.AuthorID, Author2.PublicationID
I suppose I'm not sure why you need the GROUP BY in the first place. Why couldn't you use a correlated subquery like so:
Select Author1.AuthorID
, Publications.PublicationID
, Publications.PubName
, (
Select GROUP_CONCAT(TRIM(Author2.AuthorID) ORDER BY Author2.AuthorID ASC)
From Authors As Author2
Where Author2.PublicationID = Publications.PublicationID
) AS 'AuthorsAll'
FROM Authors AS Author1
INNER JOIN Publications
ON Author1.PublicationID = Publications.PublicationID
Where Author1.AuthorId In("1","2","3")