Divide two queries in SQL then group by - sql

I am looking for the rate change between new accounts and all accounts, I have both queries listed below. I need to divide NewAccounts by AllAccounts, take that percentage and group by town in the same query. Thanks
SELECT DISTINCT Count(NewAccounts), Town
FROM (SELECT Stuff)
WHERE (Newaccounts)
Group By Town
;
SELECT DISTINCT Count(AllAccounts), Town
FROM (SELECT DifferentSTUFF)
WHERE (AllAccounts)
Group By Town

You need to rewrite your queries as subqueries and join them together:
SELECT CAST(na.NewAccounts AS FLOAT) / aa.AllAccounts
FROM ( SELECT Count(NewAccounts) AS NewAccounts, Town
FROM (SELECT Stuff)
WHERE (Newaccounts)
GROUP BY Town
) na
INNER JOIN
( SELECT Count(AllAccounts) AS AllAccounts, Town
FROM (SELECT DifferentSTUFF)
WHERE (AllAccounts)
GROUP BY Town
) aa
ON aa.Town = na.Town;
n.b. I have removed DISTINCT from both queries as it is redundant. The cast to float on NewAccounts is to avoid the implicit conversion of the result integer division back to an integer.
You may need to alter this slightly depending on the availability of data in each of the queries, i.e. if you won't always have a result in the new accounts for a town it would be better written as:
SELECT CAST(COALESCE(na.NewAccounts, 0) AS FLOAT) / aa.AllAccounts
FROM
( SELECT Count(AllAccounts) AS AllAccounts, Town
FROM (SELECT DifferentSTUFF)
WHERE (AllAccounts)
GROUP BY Town
) aa
LEFT JOIN
( SELECT Count(NewAccounts) AS NewAccounts, Town
FROM (SELECT Stuff)
WHERE (Newaccounts)
GROUP BY Town
) na
ON aa.Town = na.Town

Related

Building/Creating SQL Metrics from the table

I have a table:
SELECT aaa.sr_nbr,
aaa.inst_nbr,
bb.country,
bb.sr_control_type,
bb.it_tran_code,
ccc.cust_name,
ccc.cust_nbr
FROM tablea1 aaa
INNER JOIN tablea2 bb
ON aaa.inst_id=bb.inst_id AND aaa.item_id=bb.item_id
LEFT JOIN table3 ccc
ON bb.inst_id=ccc.inst_id AND bb.item_id=ccc.item_id
WHERE ccc.cust_name NOT LIKE '%EXP%'
AND ccc.cust_name NOT LIKE '%RMAA%' mt;
Now, I have created, separately, queries for metrics, like:
SELECT mt.sr_nbr,
mt.inst_nbr,
mt.country,
mt.sr_control_type,
mt.it_tran_code,
mt.cust_name,
mt.cust_nbr
COUNT(mt.sr_nbr) as cnt_nbr
FROM mt
WHERE mt.it_tran_code <> 'D'
GROUP BY 1,2,3,4,5,6,7;
or the another one:
SELECT t_2.sr_nbr,
t_2.inst_nbr,
t_2.country,
t_2.sr_control_type,
t_2.it_tran_code,
t_2.cust_name,
t_2.cust_nbr
SUM(t_2.sn_dup) AS sn_dup_sum
FROM (
SELECT
t_1.sr_nbr,
t_1.inst_nbr,
t_1.country,
t_1.sr_control_type,
t_1.it_tran_code,
t_1.cust_name,
t_1.cust_nbr
COUNT(t_1.sr_nbr) AS sn_dup
FROM
(
SELECT
mt.sr_nbr,
mt.inst_nbr,
mt.country,
mt.sr_control_type,
mt.it_tran_code,
mt.cust_name,
mt.cust_nbr
FROM mt
WHERE ccc.cust_name NOT LIKE '%EXP%'
AND ccc.cust_name NOT LIKE '%RMAA%'
) AS t_1
GROUP BY 1,2,3,4,5,6,7
HAVING
COUNT(t_1.sr_nbr) > 1
) AS t_2
GROUP BY 1,2,3,4,5,6,7;
and so on... I have about 10 similar metrics.
Now, I do not know the best way how to "put" those query metrics within the main table/query.
You can insert results of a SELECT query into a table if you are able to fill the INSERT statement correctly.
Example:
INSERT INTO Customers (CustomerName, City, Country)
SELECT SupplierName, City, Country FROM Suppliers
WHERE Country='Germany';
Source: https://www.w3schools.com/sql/sql_insert_into_select.asp
Make sure the amount and types of the results matches the columns you're trying to insert.

SQL query that will retrieve set containing all entries from another set

I have the following relations in my db:
Organization: information about political and economical organizations.name: the full name of the organizationabbreviation: its abbreviation
isMember: memberships in political and economical organizations.organization: the abbreviation of the organizationcountry: the code of the member country
geo_desert: geographical information about desertsdesert: the name of the desertcountry: the country code where it is locatedprovince: the province of this country
My task is to retrieve organizations which have within their members full set of countries with deserts. This organization can have also countries without deserts. So I have a set of countries with deserts and every organization in result should have all of them as members and arbitrary amount of other (no desert) countries.
I tried so far to write following code, but it doesn't work.
WITH CountriesWithDeserts AS (
SELECT DISTINCT country
FROM dbmaster.geo_desert
), OrganizationsWithAllDesertMembers AS (
SELECT organization
FROM dbmaster.isMember AS ism
WHERE (
SELECT count(*)
FROM (
SELECT *
FROM CountriesWithDeserts
EXCEPT
SELECT country
FROM dbmaster.isMember
WHERE organization = ism.organization
)
) IS NULL
), OrganizationCode AS (
SELECT name, abbreviation
FROM dbmaster.Organization
)
SELECT oc.name AS Organization
FROM OrganizationCode AS oc, OrganizationsWithAllDesertMembers AS owadm
WHERE oc.abbreviation=owadm.organization;
UPD: DBMS says: "ism.organization is not defined"
I'm using DB2/LINUXX8664 9.7.0
Output should look like this:
NAME --------------------------------------------------------------------------------
African, Caribbean, and Pacific Countries
African Development Bank
Agency for Cultural and Technical Cooperation
Andean Group
I find the easiest way to handle this is by using group by and having. You just want to focus on the deserts, so the rest of the countries don't matter.
select m.organization
from isMember m join
geo_desert d
on m.country = d.country
group by m.organization
having count(distinct m.country) = (select count(distinct d.country) from geo_desert);
The having clause simply counts the number of matching (i.e. desert) countries and checks that all are included.
Word it like this: You are looking for organizations for which not exists a desert country they don't include.
select *
from organization o
where not exists
(
select country from geo_desert
except
select country from ismember
where organization = o.abbreviation
);
Here are two equivalent solutions:
First:
WITH CountriesWithDeserts AS (
SELECT DISTINCT country
FROM dbmaster.geo_desert
), OrganizationsWithAllDesertMembers AS (
SELECT ism.organization
FROM dbmaster.isMember AS ism
JOIN CountriesWithDeserts AS cwd
ON ism.country = cwd.country
GROUP BY ism.organization
HAVING count(ism.country) = (SELECT count(*) FROM CountriesWithDeserts)
), OrganizationCode AS (
SELECT name, abbreviation
FROM dbmaster.Organization
)
SELECT oc.name AS Organization
FROM OrganizationCode AS oc, OrganizationsWithAllDesertMembers AS owadm
WHERE oc.abbreviation=owadm.organization;
Second:
WITH CountriesWithDeserts AS (
SELECT DISTINCT country
FROM dbmaster.geo_desert
)
SELECT org.name AS Organization
FROM dbmaster.Organization AS org
WHERE NOT EXISTS (
SELECT *
FROM CountriesWithDeserts
EXCEPT
SELECT country
FROM dbmaster.isMember
WHERE organization = org.abbreviation
);

SQL: multiple counts from same table

I am having a real problem trying to get a query with the data I need. I have tried a few methods without success. I can get the data with 4 separate queries, just can't get hem into 1 query. All data comes from 1 table. I will list as much info as I can.
My data looks like this. I have a customerID and 3 columns that record who has worked on the record for that customer as well as the assigned acct manager
RecID_Customer___CreatedBy____LastUser____AcctMan
1-------1374----------Bob Jones--------Mary Willis------Bob Jones
2-------1375----------Mary Willis------Bob Jones--------Bob Jones
3-------1376----------Jay Scott--------Mary Willis-------Mary Willis
4-------1377----------Jay Scott--------Mary Willis------Jay Scott
5-------1378----------Bob Jones--------Jay Scott--------Jay Scott
I want the query to return the following data. See below for a description of how each is obtained.
Employee___Created__Modified__Mod Own__Created Own
Bob Jones--------2-----------1---------------1----------------1
Mary Willis------1-----------2---------------1----------------0
Jay Scott--------2-----------1---------------1----------------1
Created = Counts the number of records created by each Employee
Modified = Number of records where the Employee is listed as Last User
(except where they created the record)
Mod Own = Number of records for each where the LastUser = Acctman
(account manager)
Created Own = Number of Records created by the employee where they are
the account manager for that customer
I can get each of these from a query, just need to somehow combine them:
Select CreatedBy, COUNT(CreatedBy) as Created
FROM [dbo].[Cust_REc] GROUP By CreatedBy
Select LastUser, COUNT(LastUser) as Modified
FROM [dbo].[Cust_REc] Where LastUser != CreatedBy GROUP By LastUser
Select AcctMan, COUNT(AcctMan) as CreatePort
FROM [dbo].[Cust_REc] Where AcctMan = CreatedBy GROUP By AcctMan
Select AcctMan, COUNT(AcctMan) as ModPort
FROM [dbo].[Cust_REc] Where AcctMan = LastUser AND NOT AcctMan = CreatedBy GROUP By AcctMan
Can someone see a way to do this? I may have to join the table to itself, but my attempts have not given me the correct data.
The following will give you the results you're looking for.
select
e.employee,
create_count=(select count(*) from customers c where c.createdby=e.employee),
mod_count=(select count(*) from customers c where c.lastmodifiedby=e.employee),
create_own_count=(select count(*) from customers c where c.createdby=e.employee and c.acctman=e.employee),
mod_own_count=(select count(*) from customers c where c.lastmodifiedby=e.employee and c.acctman=e.employee)
from (
select employee=createdby from customers
union
select employee=lastmodifiedby from customers
union
select employee=acctman from customers
) e
Note: there are other approaches that are more efficient than this but potentially far more complex as well. Specifically, I would bet there is a master Employee table somewhere that would prevent you from having to do the inline view just to get the list of names.
this seems pretty straight forward. Try this:
select a.employee,b.created,c.modified ....
from (select distinct created_by from data) as a
inner join
(select created_by,count(*) as created from data group by created_by) as b
on a.employee = b.created_by)
inner join ....
This highly inefficient query may be a rough start to what you are looking for. Once you validate the data then there are things you can do to tidy it up and make it more efficient.
Also, I don't think you need the DISTINCT on the UNION part because the UNION will return DISTINCT values unless UNION ALL is specified.
SELECT
Employees.EmployeeID,
Created =(SELECT COUNT(*) FROM Cust_REc WHERE Cust_REc.CreatedBy=Employees.EmployeeID),
Mopdified =(SELECT COUNT(*) FROM Cust_REc WHERE Cust_REc.LastUser=Employees.EmployeeID AND Cust_REc.CreateBy<>Employees.EmployeeID),
ModOwn =
CASE WHEN NOT Empoyees.IsManager THEN NULL ELSE
(SELECT COUNT(*) FROM Cust_REc WHERE AcctMan=Employees.EmployeeID)
END,
CreatedOwn=(SELECT COUNT(*) FROM Cust_REc WHERE AcctMan=Employees.EmployeeID AND CReatedBy=Employees.EMployeeID)
FROM
(
SELECT
EmployeeID,
IsManager=CASE WHEN EXISTS(SELECT AcctMan FROM CustRec WHERE AcctMan=EmployeeID)
FROM
(
SELECT DISTINCT
EmployeeID
FROM
(
SELECT EmployeeID=CreatedBy FROM Cust_Rec
UNION
SELECT EmployeeID=LastUser FROM Cust_Rec
UNION
SELECT EmployeeID=AcctMan FROM Cust_Rec
)AS Z
)AS Y
)
AS Employees
I had the same issue with the Modified column. All the other columns worked okay. DCR example would work well with the join on an employees table if you have it.
SELECT CreatedBy AS [Employee],
COUNT(CreatedBy) AS [Created],
--Couldn't get modified to pull the right results
SUM(CASE WHEN LastUser = AcctMan THEN 1 ELSE 0 END) [Mod Own],
SUM(CASE WHEN CreatedBy = AcctMan THEN 1 ELSE 0 END) [Created Own]
FROM Cust_Rec
GROUP BY CreatedBy

SQL nested aggregate functions MAX(COUNT(*))

I'm trying to select max(count of rows).
Here is my 2 variants of SELECT
SELECT MAX(COUNT_OF_ENROLEES_BY_SPEC) FROM
(SELECT D.SPECCODE, COUNT(D.ENROLEECODE) AS COUNT_OF_ENROLEES_BY_SPEC
FROM DECLARER D
GROUP BY D.SPECCODE
);
SELECT S.NAME, MAX(D.ENROLEECODE)
FROM SPECIALIZATION S
CROSS JOIN DECLARER D WHERE S.SPECCODE = D.SPECCODE
GROUP BY S.NAME
HAVING MAX(D.ENROLEECODE) =
( SELECT MAX(COUNT_OF_ENROLEES_BY_SPEC) FROM
( SELECT D.SPECCODE, COUNT(D.ENROLEECODE) AS COUNT_OF_ENROLEES_BY_SPEC
FROM DECLARER D
GROUP BY D.SPECCODE
)
);
The first one is working OK, but I want to rewrite it using "HAVING" like in my second variant and add there one more column. But now 2nd variant don't output any data in results, just empty columns.
How can I fix it ? Thank YOU!)
This query based on description given in comments and some suggestions, so it may be wrong:
select -- 4. Join selected codes with specializations
S.Name,
selected_codes.spec_code,
selected_codes.count_of_enrolees_by_spec
from
specialization S,
(
select -- 3. Filter records with maximum popularity only
spec_code,
count_of_enrolees_by_spec
from (
select -- 2. Count maximum popularity in separate column
spec_code,
count_of_enrolees_by_spec,
max(count_of_enrolees_by_spec) over (partition by null) max_count
from (
SELECT -- 1. Get list of declarations and count popularity
D.SPECCODE AS SPEC_CODE,
COUNT(D.ENROLEECODE) AS COUNT_OF_ENROLEES_BY_SPEC
FROM DECLARER D
GROUP BY D.SPECCODE
)
)
where count_of_enrolees_by_spec = max_count
)
selected_codes
where
S.SPECCODE = selected_codes.spec_code
Also query not tested and some syntax errors are possible.

MySQL intersection in subquery

I'm trying to create a filter for a list (of apartments), with a many-to-many relationship with apartment features through the apartsments_features table.
I would like to include only apartments that have all of some features (marked 'Yes' on a form) excluding all the ones that have any of another set features (marked 'No'). I realized too late that I couldn't use INTERSECT or MINUS in MySQL.
I have a query that looks something like:
SELECT `apartments`.* FROM `apartments` WHERE `apartments`.`id` IN (
SELECT `apartments`.`id` FROM `apartments` INTERSECT (
SELECT `apartment_id` FROM `apartments_features` WHERE `feature_id` = 103
INTERSECT SELECT `apartment_id` FROM `apartments_features` WHERE `feature_id` = 106
) MINUS (
SELECT `apartment_id` FROM `apartments_features` WHERE `feature_id` = 105 UNION
SELECT `apartment_id` FROM `apartments_features` WHERE `feature_id` = 107)
)
ORDER BY `apartments`.`name` ASC
I'm pretty sure there's a way to do this, but at the moment my knowledge is restricted to little more than simple left and right joins.
A slightly different way of doing it:
select a.*
from apartments a
join apartments_features f1
on a.apartment_id = f1.apartment_id and f1.feature_id in (103,106) -- applicable features
where not exists
(select null from apartments_features f2
where a.apartment_id = f2.apartment_id and f2.feature_id in (105,107) ) -- excluded features
group by f1.apartment_id
having count(*) = 2 -- number of applicable features
You could try something like this:
SELECT apartment_id
FROM
(
SELECT apartment_id
FROM apartments_features
WHERE feature_id IN (103, 106)
GROUP BY apartment_id
HAVING COUNT(*) = 2
) T1
LEFT JOIN
(
SELECT apartment_id
FROM apartments_features
WHERE feature_id IN (105, 107)
) T2
ON T1.apartment_id = T2.apartment_id
WHERE T2.apartment_id IS NULL
Join the result of this query to the apartments table to get the name, etc.