I'm having trouble trying to get my query working as intended, I have two tables (names and classes). Where names has two rows (username and name) & classes has three rows (class, username and time).
I'm trying to extract username, name and the amount of classes each person teaches (see table below), which can be figured out by looking at how many times a given "username" appears in the "classes" table. The problem is that there are some usernames that appear in the "names" table but don't appear in the "classes" table, these users should be assigned "0" in the count. There's also users who appear in the classes table but don't appear in the names table, these people should still be taken into account but with a null value as their name.
names table classes table
__________________ ________________________
|username | name | |class | username |time |
|ab | Ali | |xxx | ch |xxx |
|ch | Chi | |xxx | dd |xxx |
|dd | Dia | |xxx | ee |xxx |
etc...
desired output from query
__________________________________________
|username | name |number of classes taught|
|ab | Ali |0 |
|ch | Chi |1 |
|dd | Dia |3 |
|ee | null |5 |
etc....
I have the following query so far
SELECT
names.username,
name,
COUNT(*)
FROM
classes
INNER JOIN names ON
names.username = classes.username
GROUP BY
classes.username
ORDER BY
COUNT(*)
actual output from query
__________________________________________
|username | name |number of classes taught|
|ch | Chi |1 |
|dd | Dia |3 |
But this gives me an output that doesn't include users who don't teach and also excludes users who don't have a real name. I'm not sure how to resolve this issue, would I need to combine the two username rows without creating duplicates? or something along those lines?
If you want all users, start with the user table. If you want all users that don't have any classes, do a LEFT join and not an INNER join
SELECT
names.username,
name,
COUNT(*)
FROM
names
LEFT JOIN classes ON
names.username = classes.username
GROUP BY
classes.username
ORDER BY
COUNT(*)
What you need is a FULL OUTER join which is not supported by SQLite, but can be emulated with LEFT joins and UNION ALL and then aggregate:
select username, name, count(*) [number of classes taught]
from (
select n.username, n.name, c.class
from names n left join classes c
on c.username = n.username
union all
select c.username, n.name, c.class
from classes c left join names n
on c.username = n.username
where n.username is null
)
group by username, name
See a simplified demo.
Related
I tried searching this kind of use case on google but did found exact what i was looking so, Need help to reach to specific point by useful answer, docs or any reference
I have this situation: table A has to be joined with table B and table C; B and C have similar columns, so there will be duplicate column names in select part, however give preferences to all the data of tables B if available otherwise show data from table c
For example:
SELECT
ae.*, ml.name as name, ml.contact AS contact,
ve.name AS name, ve.contact AS contact
FROM
TABLE ae
LEFT JOIN
TABLE ml ON ae.eid = ml.eid
LEFT JOIN
TABLE ve ON ae.eid = ve.eid
WHERE
ae.eid = 1
ml data
eid | name | contact
----+------+--------
1 | xyz | null
ve data
eid | name | contact
----+------+--------
1 | xyz | 1
ae data
eid | gender
----+--------
1 | male
I want this result:
eid | gender | name | contact
----+--------+------+--------
1 | male | xyz | null
But I am getting this for now:
eid | gender | name | contact | contact
----+--------+------+---------+--------
1 | male | xyz | 1 | null
I'm using node-mssql driver for querying SQL Server data.
Thanks,
You must join ve only if there is no matching row in ml and you do it if you add in the ON clause the condition ... AND ml.eid IS NULL.
Also use COALESCE() to select the columns from ml first and if they don't exist from ve:
SELECT ae.*,
COALESCE(ml.name, ve.name) AS name,
COALESCE(ml.contact, ve.contact) AS contact
FROM ae
LEFT JOIN ml ON ae.eid = ml.eid
LEFT JOIN ve ON ae.eid = ve.eid AND ml.eid IS NULL
WHERE ae.eid = 1
See the demo.
Results:
eid
gender
name
contact
1
male
xyz
null
My database has a table of Products, like so:
PRODUCTS
----------------------------
id | name | suppliers
----------------------------
1 | widget | {1,2}
2 | gizmo | {1}
3 | geegaw | {3}
4 | tchotchke | null
The suppliers column contains an array (numeric[]) of IDs belonging to a table of suppliers:
SUPPLIERS
------------
id | name
------------
1 | alpha
2 | beta
3 | gamma
How can I write a query that will return the contents of PRODUCTS except with an array of supplier names instead of supplier ID numbers? Result should look like this:
-----------------------------------
id | name | suppliers
-----------------------------------
1 | widget | {'alpha','beta'}
2 | gizmo | {'alpha'}
3 | geegaw | {'gamma'}
4 | tchotchke | null
Succinct and efficient methods would be preferred, but readability/understandability are also nice.
Edit: This isn't a duplicate of the linked question, although that question does involve the unnest operation, it doesn't re-aggregate the result. The answer to this question makes a new contribution to the site.
select t1.id, t1.name, array_agg(s.Name) as Suppliers
from Products t1
left join lateral (
select unnest(suppliers) as supplierId from myProducts t2
where t1.id = t2.id) as t on true
left join Suppliers s on s.Id = t.supplierID
group by t1.id, t1.name;
What was I thinking. Here is a better version:
select p.id, p.name, array_agg(s.Name) as Suppliers
from (select *,unnest(suppliers) as supplierId from Products) p
left join Suppliers s on s.Id = p.supplierID
group by p.id, p.name;
I have an old data table (in MS ACCESS, if you can believe it) that is supposed to be 'related products' from an older ecommerce store. I'm trying to salvage these related products for my new store.
The dataset with the following fields/data sample:
+---------+------------+-----------------+
| GroupID | ProductId | Sku |
+---------+------------+-----------------+
| 1001 | 12473 | C2S-44682-AMB |
| 1001 | 3628 | C-43604-1 |
+---------+------------+-----------------+
The "groupID" is the association -- productIds in the same group are related to each other. So these two products are related to each other because they both belong in GroupId 1001. There are some 3500 rows of data total.
What I need is to export these related products into a new table so that I can import into the new store and retain the related relationship. The new data needs a different formatted structure:
ParentId (the first product), ChildId (the second, related product)
So -- using my example from above:
12473, 3628 (the first product should display the second)
3628, 12473 (the second product should display the first
I'm not sure how to author the correct SQL query to locate, loop through, and write these new records into a new DB.
I thought perhaps a "For/Each" loop, but in looking for references, I couldn't seem to locate the proper context (lots of PHP examples, but I'm not strong in PHP and really think there has to be a SQL method to do this). You can run aggregates on "having" clauses on SQL, but again, that didn't seem right to me either.
Any suggestions on how to proceed?
No need for a loop... just a self-join. Notice I added some records to the test data for a more in depth example.
declare #oldTable table (GroupID int, ProductId int, Sku varchar(64))
insert into #oldTable
values
(1001,12473,'C2S-44682-AMB'),
(1001,3628,'C-43604-1'),
(1001,4896,'C-43-558604-1'),
(1099,4458,'C-xxx-1'),
(1099,5217,'C-asbf3-1')
select
t1.ProductId as parent
,t2.ProductId as Child
from
#oldTable t1
left join
#oldTable t2 on
t1.GroupID = t2.GroupID
and t1.ProductId <> t2.ProductId
RETURNS
+--------+-------+
| parent | Child |
+--------+-------+
| 12473 | 3628 |
| 12473 | 4896 |
| 3628 | 12473 |
| 3628 | 4896 |
| 4896 | 12473 |
| 4896 | 3628 |
| 4458 | 5217 |
| 5217 | 4458 |
+--------+-------+
You need a self-join where you match all products to all other products in the group, but exclude the initial product. I believe this will do the trick for you.
Just replace dbo.Product with your actual table name.
select
ProductID = P.ProductID,
RelatedProductID = R.ProductID
from dbo.Product P
join dbo.Product R on P.GroupID = R.GroupID and P.ProductID != R.ProductID
Language: T-SQL
Server: SQL Server 2008 R2 - SQL Server 2014
I have what, based on searching here an elsewhere, appears to either be a unique problem or I can't properly verbalize what I'm trying to accomplish. I'd like to query across multiple dissimilar tables that have dissimilar field structures and JOIN them to a single other table. We have a table of ASSETS tb_assets and a table of LICENSES tb_licenses. I'd like to query across both of these and JOIN them to the table of VENDORS tb_vendors.
Like this:
---------------------- ---------------------------
| TB_ASSETS | | TB_LICENSES |
---------------------- ---------------------------
| f_assetvendor | <~~~ ~~~> | f_licensevendor |
| f_assettag | | | | f_licensename |
| f_assetname | | | | f_licenseexpirationdate |
| | | | | f_licensequantity |
---------------------- | | ---------------------------
| |
~~~~~~~~ ~~~~~~~~~~
| ---------------------- |
| | TB_VENDORS | |
| ---------------------- |
~~> | f_vendorGUID | <~~
| f_vendorname |
----------------------
For a short example, I want to search for a vendor name (f_vendorname) of Amazon, I'd like to query against tb_assets as well as against tb_licenses. The query I tried below errors with Invalid column name 'f_assetvendor', so I'm doing something wrong.
SELECT
f_assetvendor AS 'AssetVendor', f_licensevendor as 'LicenseVendor'
FROM
tb_assets, tb_licenses
LEFT JOIN
tb_vendors assven ON assven.f_vendorGUID = f_assetvendor
LEFT JOIN
tb_vendors licven ON licven.f_vendorGUID = f_licensevendor
WHERE
f_vendorname LIKE '%Amazon%'
Regarding my title stating "not UNION", I can't use a UNION here because with UNION column names for the final result set are taken from the first query, the columns must have the same data types, and both tables must have the same number of columns.
Give this a go;
SELECT
v.f_vendorGUID,
v.f_vendorname,
a.f_assetvendor AssetVendor,
l.f_licensevendor LicenseVendor
FROM
TB_VENDORS v
JOIN
TB_ASSETS a ON v.f_vendorGUID = a.f_assetvendor
JOIN
TB_LICENSES l ON v.vendorGUID = l.f_licensevendor
WHERE
v.vendorname LIKE '%Amazon%'
You can use the TB_VENDORS as the main table and join the other two tables to it, in this instance (inner join) there's no particular order they should be in. You've shown in your diagram that there's a join between these tables. If you have the chance of missing data in either TB_ASSETS or TB_LICENCES, use LEFT JOIN instead of JOIN.
Please get out of the habit of that old style join you've used in your FROM statement, it's a really old way of doing it.
I know you said no to a union statement but I think this gives you what you need unless I am misunderstanding your query.
Select [t].[Vendor]
, [t].[VendorType]
From ( Select [f_assetvendor] As 'Vendor'
, 'Asset' As 'VendorType'
From [tb_assets]
Left Join [tb_vendors] [assven]
On [assven].[f_vendorGUID] = [f_assetvendor]
Union All
Select [f_licensevendor] As 'Vendor'
, 'License' As 'VendorType'
From [tb_licenses]
Left Join [tb_vendors] [licven]
On [licven].[f_vendorGUID] = [f_licensevendor]
) [t]
Where [t].[Vendor] Like '%Amazon%';
However you get there, you will need to make your tables similar enough to report together.
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')