SQL Server: Parent id with the least number of children - sql-server

I have two tables Client and Instructor.
Client table :
id_client|name_client|FK_instructor
---------+-----------+------------
1 | Clinton | 2
2 | Gates` | 1
3 | Bush | 1
4 | Clinton | 2
5 | Obama | 1
6 | Jack | 3
Instructor table :
id_instructor|name_instructor
-------------+---------------
1 | Sara
2 | Sam
3 | Dean
4 | Julie
5 | Jake
I want to select the 3 instructors who have the least number of clients associated.
Thank you in advance.

Now that you mentioned you're using SQL Server, in addition to the GROUP BY and ORDER BY you need a TOP(3) on your SELECT.
SELECT TOP(3) i.id_instructor, i.name_instructor
FROM Instructor i
JOIN Client c ON c.FK_instructor = i.id_instructor
GROUP BY i.id_instructor, i.name_instructor
ORDER BY COUNT(*) --Implicitly ascending
Note that I added the instructor id to the group by compared to the other answer in case more than one instructor has the same name.

If you are working with Netezza, you could try:
SELECT name_instructor, COUNT(id_client)
FROM instructor_table
JOIN client_table on instructor_table.id_instructor = client_table.FK_instructor
GROUP BY name_instructor
ORDER BY COUNT(id_client) DESC
LIMIT 3
There is great documentation for Netezza here:
http://www-304.ibm.com/support/knowledgecenter/SSULQD_7.2.0/com.ibm.nz.dbu.doc/c_dbuser_sql_grammar.html
There are also SQL tutorials here:
http://www.w3schools.com/sql/

Related

SQL - How can I get the number of duplicates in the non-aggregated result?

Suppose I have a table tb such that
select * from tb
returns
ID | City | Country
1 | New York | US
2 | Chicago | US
3 | Boston | US
4 | Beijing | China
5 | Shanghai | China
6 | London | UK
What is the easiest way to write a query that can return the following result?
ID | City | Country | Count
1 | New York | US | 3
2 | Chicago | US | 3
3 | Boston | US | 3
4 | Beijing | China | 2
5 | Shanghai | China | 2
6 | London | UK | 1
The only solution I can think of is
with cte as (select country, count(1) as Count from tb group by country)
select tb.*, cte.Count from tb join cte on tb.Country = cte.Country
But I feel that is not succinct enough. I am wondering if there is anything like Duplicate_Number() over (partition by country) to do this.
Try this:
select *
,COUNT(*) OVER (PARTITION BY Country)
from tb
The OVER clause
Determines the partitioning and ordering of a rowset before the
associated window function is applied.
So, we are basically telling to COUNT the records, but to group the rows per COUNTRY.
Another approach to achieve the result :
select t1.*, t2.Country_Count from tb t1
join
(select country, count(country) Country_Count from tb group by country) t2
on t1.country=t2.country
order by t1.id
SQL HERE

SQL Server: REPLACE comma and combine it with similar row

I have a bad table structure. I try to modify it as little as possible because the real problem is more complicated. I'm working in SQL Server 2005.
Here is my table:
tbl_item
id | item
---+------
1 | car
2 | car
3 | car, => This is what I focus
4 | jet
5 | jet, car => This is just an example of the comma purpose
Query :
SELECT item, count(*) AS sum
FROM tbl_item
GROUP BY item
ORDER BY sum DESC
Result:
item | sum
--------+-------
car | 2
car, | 1
jet | 1
jet,car | 1
What I want is like this:
item | sum
---------+--------
car | 3
jet | 1
jet, car | 1 => actually I don't care about this, but this just for example
I tried :
SELECT REPLACE(item,',',''), count(*) AS sum
FROM tbl_item
GROUP BY item
ORDER BY sum DESC
But the result is:
item | sum
---------+------
car | 2
car | 1 => still in the different row
jet | 1
car, jet | 1
I can manipulate this so easy with PHP, but I wonder how to do this with pure SQL Server.
Thanks in advance!
Try:
GROUP BY REPLACE(item,',','')
This will normalize the items, then group on them.
You need to add it to the group by clause:
SELECT REPLACE(item,',',''), count(*) AS sum
FROM tbl_item
GROUP BY replace(item,',','')
ORDER BY sum DESC

Unpivot SQL each row in SQL table to key-value pairs with group IDs

Running SQL Server 2012, I have a table in the following format:
ENC_ID | Name | ED_YN | Seq
-------------------------------------
1234 | John | Y | 1
1234 | Sally | N | 2
2345 | Chris | N | 1
2345 | Sally | N | 2
I would like to unpivot this into a entity-attribute-value list (if that's the right terminology - I am thinking of them as key-value pairs grouped by IDs), with the following format:
ENC_ID | Seq | Key | Value
--------------------------------------
1234 | 1 | Name | John
1234 | 1 | ED_YN | Y
1234 | 2 | Name | Sally
1234 | 2 | ED_YN | N
2345 | 1 | Name | Chris
2345 | 1 | ED_YN | N
2345 | 2 | Name | Sally
2345 | 2 | ED_YN | N
I have seen various answers to this using UNION or UNPIVOT, but these solutions tend to be long and must be closely customized to the table. I'm looking for a solution that can be reused without a great deal of rewriting as this pattern solves a problem I expect to run into frequently (ingesting data from star-schema into Tableau via extracts, where I don't necessarily know the number of value columns).
The closest thing I've found to solve this is this question/answer but I haven't had success altering the solution to add ENC_ID and Seq to each row in the result table.
Thanks.
I will use Cross Apply with table valued Constructor to do this unpivoting
select ENC_ID, Seq, Key, Value
from yourtable
cross apply
(values ('Name',Name),
('ED_YN',ED_YN)) CS (Key,Value)
You can try below query. This uses classic UNPIVOT syntax.
Since I am unsure about your column types I have casted both of them as varchar(100). You can increase length from 100.
Working sql fiddle demo link :
select enc_id,seq, k [key],v [value] from
(select enc_id,seq,cast(name as varchar(100)) as name, cast(ed_yn as varchar(100)) as ed_yn from r)s
UNPIVOT
( v for k in ([name],[ed_yn])
)up

How to use field value of joined table for join rather than foreign key

I have a new database schema that I need to query and I am having trouble getting the data I need without using a primary key in the JOIN. I didn't design the schema but I have to work with it. I tried creating a SQLFiddle for this but it kept giving me a gateway error when I tried.
Basically I have a Contacts table that contains ALL the contacts stored in the database. The Contacts table contains two fields that reference the tables that the contact is linked to (Vendor and Client). The Contact table also contains a field named "ContactType". This links to another table that defines the type of Contact (Owner or Employee).
[Client]
ClientID | Name
==============================
1 | Uptown Dining
2 | Downtown Eatery
3 | Midtown Steakhouse
[Vendor]
VendorID | Name
==============================
1 | ABC Produce
2 | DEF Seafood
3 | GHI Meats
[ContactType]
ContactTypeID | Name
==============================
1 | Owner
2 | Employee
[Contact]
ContactID | TableName | TableID | ContactTypeID | FirstName | LastName
========================================================================
1 | Client | 1 | 1 | Bob | Smith
2 | Vendor | 1 | 1 | Jill | Weston
3 | Vendor | 1 | 2 | Alice | Jenkins
4 | Client | 2 | 1 | Chris | Brown
5 | Vendor | 3 | 2 | Todd | Davis
What I am trying to do is get a list of Client contacts who are the owners of their company. This SQl works to do just that.
SELECT
Contact.FirstName,
Contact.LastName
FROM Client
LEFT JOIN Contact
ON Client.ClientID = Contact.TableID
AND Contact.TableName = 'Client'
AND Contact.ContactTypeID = 1
This would give me ..
Bob Smith
Chris Brown
The problem is I don't want to use AND Contact.ContactTypeID = 1 in the JOIN. I want to be able to use Owner or Employee in place of the primary key (1 or 2) of the ContactType table in the JOIN but I am unsure of how to do this. Please keep in mind the restriction needs to be in the JOIN since I need to reserve the WHERE clause to filter specific clients.
For example what if I wanted to query for a specific Vendor and get a list of the owner and employee in the same row ...
Vendor Name | Owner First | Owner Last | Employee First | Employee Last
============================================================================
ABC Produce | Jill | Weston | Alice | Jenkins
I'm not sure I understand your problem correctly, but if I'm reading it right - you want to have the chance to specify "Owner" or "Employee" explicitly in your WHERE clause rather than 1 or 2.
Perhaps this is what you are looking for:
SELECT
Contact.FirstName,
Contact.LastName
FROM Client
LEFT JOIN Contact
ON Client.ClientID = Contact.TableID
AND Contact.TableName = 'Client'
LEFT JOIN ContactType ct ON ct.ContactTypeID = Contact.ContactTypeID
-- Now you can use it directly, as below
WHERE ct.Name = 'Owner'
Edit: Response to your comment
Not without getting messy... You can use a derived inline table, or a view, but all of these would be performance killers. The least performance-impairing (but still ugly) way I can think of is something like this:
SELECT
Contact.FirstName,
Contact.LastName
FROM Client
LEFT JOIN Contact
ON Client.ClientID = Contact.TableID
AND Contact.TableName = 'Client'
AND Contact.ContactTypeID = (SELECT iix.ContactTypeID FROM ContactType iix WHERE iix.Name = 'Owner')

SQL Server 2000 - How do I rotate the results of a join in the final results of a query?

My database is quite complex so I've simplified my problem down to the tables below.
TableA and TableB are related by the NameID field in TableB. I am trying to create a SQL statement to produce the desired results. I'm understand JOINs and how they work but I can't fogure this out.
There will never be more than 2 items in TableB for each item in TableA. There could be less than 2 items.
This will be used on a SQL Server 2000 server.
TableA
ID | Name
---+-----
1 | John
2 | Jane
3 | Bob
4 | Doug
TableB
ID | NameID | Information
---+--------+------------
1 | 1 | Apples
2 | 1 | Apples
3 | 2 | Pears
4 | 2 | Grapes
5 | 3 | Kiwi
Desired Result
ID | Name | InformationA | InformationB
---+------+--------------+-------------
1 | John | Apples | Apples
2 | Jane | Pears | Grapes
3 | Bob | Kiwi | NULL
4 | Doug | NULL | NULL
(Edited to give the preferred ordering for the two columns)
SELECT a.Id,
a.Name,
STUFF(MIN(STR(b.Id, 10) + b.Information), 1, 10, '') AS InformationA,
CASE
WHEN COUNT(b.Id) = 2 THEN STUFF(MAX(STR(b.Id, 10) +
b.Information), 1, 10, '')
END AS InformationB
FROM TableA a
LEFT JOIN TableB b
ON a.Id = b.NameId
GROUP BY a.Id,
a.Name
I think what you need to do is a pivot. Take a look and see if that suits your needs.

Resources