SQL employee / company/country - sql

I have three tables
Country (country id - country name - country pop)
City (city id - city name - city code - city pop)
Company (city code - company name - company employee)
Company employee is the number of employee.
I must present a table with the SUM of the employees that has each company in country level.
I have used the following query
SELECT country_name, company_name, company_employee
FROM country,
city,
company
WHERE country.country_id = city.country_id
and city.city_code = company.city_code
I took a table in which you can see the country, the company and the number of employee
(e.g. In one country UK the same company ACS has 2 records ACS 300 EMPLOYEE & ACS 100 EMPLOYEE instead of one record ACS 400 employee)
I believe that something is missing from my code. I have tried use functions such as sum and group but it failed.

Is this what you want?
SELECT cy.country_name, co.company_name, SUM(co.company_employee)
FROM company co JOIN
city ci
ON ci.city_code = co.city_code JOIN
country cy
ON cy.country_id = ci.country_id
GROUP BY cy.country_name, co.company_name;
Note the use of proper, explicit, modern, standard JOIN syntax. Never use commas in the FROM clause. This is particularly important if you are learning SQL; you should learn to use the language correctly.

Related

SQL query to list cities in which employee did not work

SQL query to list cities in which employee did not work from below
"employee" table:
name
city
srini
seattle
ross
atlanta
rich
redmond
Example: if I give "srini", query should return "Atlanta" and "Redmond"
I tried below 2 queries with no luck, it returns empty results:
SELECT t1.city
FROM employee t1
JOIN employee t2 ON t1.name=t2.name
WHERE t1.city != t2.city
WHERE name='srini'
SELECT city
FROM (SELECT city FROM employee WHERE name='srini') as e1
WHERE city <> e1.city
This should work:
select distinct city
from employee
where city not in
(select city
from employee
where name = 'srini')
Basically it's selecting all city names that don't exist in a row where name is 'srini'
SQL is such fun. I'd go with a GROUP BY query, where I use the HAVING clause to only return cities where no srini lives.
select city
from employee
group by city
having sum(case when name = 'srini' then 1 else 0 end) = 0
Core ISO/ANSI SQL, i.e. every dbms is expected to support it.
Or use EXCEPT:
select city from employee
EXCEPT
select city from employee where name = 'srini'
Core ISO/ANSI SQL, i.e. every dbms is expected to support it.

How to select all rows but join information under condition

Simplified I have two tables NAMES (with columns ID and NAME) and ADRESSES (with columns COUNTRY and CITY and NAME_ID).
Example for NAMES:
1 - Hans
2 - Mark
3 - Joseph
Example for ADRESSES:
Denmark - Kopenhagen - 1
Germany - Berlin - 3
I need to select all Names which either have no adress at all OR with their CITY but only when their country is... Denmark.
SELECT
NA.NAME AS NAME,
AD.CITY AS CITY
FROM NAMES AS NA
LEFT JOIN ADRESSES AS AD ON AD.NAME_ID = NA.ID
now when I add something like
WHERE AD.COUNTRY="Denmark"
or
WHERE (AD.COUNTRY="Denmark" OR AD.COUNTRY=NULL)
I still only get a list of Names with Cities in Denmark but not all other Names which have no Adress/City at all.
When I remove the condition of course I get all Names and existing Cities but even in all other countries.
The desired result would be:
Hans - Kopenhagen
Mark - NULL
I guess you mean WHERE AD.COUNTRY='Denmark' not WHERE AD.CITY='Denmark'. Also, Using WHERE clause with LEFT JOIN makes it like INNER JOIN. So shift your condition to LEFT JOIN clause -
SELECT NA.NAME AS NAME,
AD.CITY AS CITY
FROM NAMES AS NA
LEFT JOIN ADRESSES AS AD ON AD.NAME_ID = NA.ID
WHERE AD.COUNTRY='Denmark' OR AD.COUNTRY IS NULL
I need to select all Names which either have no adress at all OR with their CITY but only when their country is... Denmark.
I believe you want:
SELECT NA.NAME AS NAME, AD.CITY AS CITY
FROM NAMES NA LEFT JOIN
ADRESSES AD
ON AD.NAME_ID = NA.ID
WHERE AD.COUNTRY = 'DENMARK' OR AD.COUNTRY IS NULL;
This should not return names in other countries, such as Germany.

Having count in SELECT clause

Find the names of cities that hosts both SALES and TRANSPORT departments
For my oracle database have this table
i.) DEPTLOC
//DEPTLOC
CITY DNAME
---------------------
NEWYORK IT
NEWYORK COMPUTER
LONDON Science
LONDON SALES
LONDON TRANSPORT
For my SQL select statement
SELECT CITY FROM DEPTLOC
WHERE
DEPTLOC.DNAME='SALES' OR DEPTLOC.DNAME='TRANSPORT'
GROUP BY
CITY
HAVING COUNT(*)=2;
the output always display
no rows selected.
My output should be
DNAME
--------
LONDON
For things like this, I try to use a simple join to the same table. First, I would have an index on the table by (City, DName).. then
select
d.City
from
deptLoc d
JOIN deptLoc d2
on d.city = d2.city
AND d2.dname = 'TRANSPORT'
where
d.dname = 'SALES'
It may look strange, but think about it. The outer portion WHERE clause only cares about cities that have ONE of the qualifiers. Why even count cities that dont even have that. So, now the join. Since you know the first qualifier on SALES is covered, re-join to the dept loc table again, but on the same city name AND the second instance is ALSO that of your 'TRANSPORT' component. You will be surprised at how fast it would be, especially on a large dataset.
can you try this :
SELECT CITY FROM DEPTLOC
WHERE
trim(DEPTLOC.DNAME)='SALES' OR trim(DEPTLOC.DNAME)='TRANSPORT'
GROUP BY
CITY
HAVING COUNT(*)=2;
I think in your data you have some different chars like space or new lines
you can check it
i think this could be a better query.
SELECT CITY
FROM DEPTLOC
WHERE
DEPTLOC.DNAME in ('SALES','TRANSPORT')
GROUP BY CITY

Modifying a SELECT query

I have a query like this:
SELECT initials, name
FROM employee e, projects p
WHERE e.country = p.country
Until now, both tables used an abbreviation for the country columns. Like "SWE" for Sweden and "ITA" for Italy.
In the future, the employee table will use names for the country columns. Like "Sweden" and "Italy".
Is it somehow possible to change my query so it can match abbreviations with names? Like "SWE" = "Sweden" and "ITA" = "Italy".
Thanks.
It would be better to have an own country table and the other tables referencing to that.
country table
-------------
id
name
abbreviation
I'd say the best solution is creating a third table where you match the current abbreviation with the full country name. You can then join both tables on that.
CountryTable (countryAbbreviation, countryName)
The select would then be something like this:
SELECT initials, name
FROM employee e JOIN countryTable c ON c.countryName = c.country
JOIN projects p ON p.country = c.countryAbbreviation
Although I fafor the solution by juergen, another solution will be altering the two tables to the new format.
UPDATE employee
SET country = "SWEDEN"
WHERE country = "SWE"
Do this for all the countries you have.
Always in country table, if country name starts with first three letters of the country in the employee table then you can use substring operator
SELECT initials, name
FROM employee e, projects p
WHERE upper(substring(e.country,1,3)) = upper(p.country)

Selecting distinct value pairs in EAV

I'm working on a user database where the profile data has been changed from a simple table into a Entity-Attribute-Value table.
Where as before the structure was along these lines:
userid (int)
address 1 (varchar)
city (varchar)
country (varchar)
It's now along these lines:
userid (int)
key (varchar)
value (varchar)
eg
userid key value
150 city London
150 country UK
151 city New York
151 country USA
152 country Mexico
I need to get a distinct list of city / country pairs and a count of all users for each country:
city country count
London UK 18
New York USA 25
There is no guarantee each key value pair will exist for each user, i.e there could be city, or country or both or neither as well as any number of other key values pairs.
This was straightforward with the old structure, but I can't even think how to begin on this, and would be grateful for some pointers
Your best solution is to go back to the traditional table because EAV makes most querying much harder than it should be - witness your problems here. You're going to be doing self-joins until you're sick of them, remanufacturing the table structure that allows you to perform sensible queries.
Cities and countries for each user ID:
SELECT a.userID, a.value AS city, b.value AS country
FROM EAV AS a
JOIN EAV AS b ON a.UserID = b.UserID
WHERE a.key = 'city'
AND b.key = 'country';
So, you end up with:
SELECT city, country, count(*)
FROM (SELECT a.userID, a.value AS city, b.value AS country
FROM EAV AS a
JOIN EAV AS b ON a.UserID = b.UserID
WHERE a.key = 'city'
AND b.key = 'country'
) AS c
GROUP BY city, country;
If there's a chance that someone might have two city or two country records, this will give you a Cartesian product with as many rows for that user as the product of the number of city and country records for that user.
This quite deliberately and consciously ignores users who have a city and no country or a country and no city (let alone those who have neither). Extending the solution to deal with those is only modestly painful - you end up with a 3-way UNION, I think, though you might be able to devise something with multiple left outer joins. But the fact that data can be entered into an EAV system without the necessary constraints to ensure that there is a city and a country for a user is simply one of the many reasons for rejecting EAV.
I'm sorry you had this foisted on you. I recommend looking at http://careers.stackoverflow.com/ as a way out of your pain, for this is only the beginning of it.
Dealing with users without either city or country or both. I think this will more or less do it:
SELECT a.userID, b.value AS city, c.value AS country
FROM (SELECT DISTINCT UserID FROM EAV) AS a
LEFT JOIN EAV AS b ON a.UserID = b.UserID
LEFT JOIN EAV AS c ON a.UserID = c.UserID
WHERE b.key = 'city'
AND c.key = 'country';
This should give you one record per user as long as there are no multiple city or country records for that user. The a scan gives you the list of unique user IDs that exist in the EAV table; the two outer joins give you the corresponding city or cities and corresponding country or countries for each such user ID, with nulls being generated if there is no city record or country record (or both) for the given user ID.
re: I need to get a distinct list of city / country pairs
SELECT DISTINCT country,city
FROM
(SELECT DISTINCT userid, VALUE AS country FROM TABLE WHERE KEY = 'country') country INNER JOIN
(SELECT DISTINCT userid, VALUE AS city FROM TABLE WHERE KEY = 'city') city ON
country.userid = city.userid
--count of all users for each country
SELECT VALUE AS country,
COUNT(DISTINCT userid) AS user_count
FROM TABLE
WHERE KEY = 'country'
GROUP BY
VALUE