Sum by groups between 2 tables - sql

I, im trying to Sum by groups between 2 tables. Basically im trying to show only the Table 'Count' with each SUM() of Table 'MoneyMovements' group by 'Bank_code'. Is this possible?. I tryed with Left join (including the 'MoneyMovements'in 'Count'). but i dont understand how to separe the sum... Any suggestion?. Im using ACCESS 2007 in VB.NET
Table 'Count'
+----+--------------+
|Code|Bank |
+----+--------------+
|1 |MACRO |
+----+--------------+
|2 |Santender Rio |
+----+--------------+
|3 |Galicia |
+----+--------------+
Table 'MoneyMovements'
+-----+--------------+
|Money|Bank_code |
+-----+--------------+
|200 |1 |
+-----+--------------+
|300 |1 |
+-----+--------------+
|0 |2 |
+-----+--------------+
|500 |3 |
+-----+--------------+
|100 |3 |
+-----+--------------+
Response i Want:
+-----+--------------+
|Money|Bank |
+-----+--------------+
|500 |MACRO |
+-----+--------------+
|0 |Santender Rio |
+-----+--------------+
|600 |Galicia |
+-----+--------------+

You need join with group by like:
Select c.Code, sum(m.Money) as Money
From Count c
inner join MoneyMovements m on c.Code = m.Bank_code
group by c.Code

Use "Left join" to get all the "Count" rows and Nz() to display 0 when there are no transactions in an account:
Select c.Code, Nz(sum(m.Money),0) as Money
From Count c
LEFT JOIN MoneyMovements m on c.Code = m.Bank_code
group by c.Code
Access really didn't like the table name "Count" so I used Counts instead, this is what I did:
SELECT Counts.code, Nz(Sum(MoneyMovements.Money),0) AS SumOfMoney
FROM Counts LEFT JOIN MoneyMovements ON Counts.Code = MoneyMovements.bank_code
GROUP BY Counts.code;

Related

How to join two queries with count?

I have 3 tables in database:
Table 1: violation
| violation_id | violation_name |
|:-------------:|:--------------:|
|1 | No Parking |
|2 | Speed Contest |
|3 | No Helmet |
Table 2: violators
| violator_id | violation_id |
|:-------------:|:--------------:|
|1 |1 |
|2 |1 |
|3 |3 |
Table 2: previous_violator
| prev_violator_id| violation_id |
|:---------------:|:--------------:|
|1 |1 |
|2 |2 |
|3 |2 |
This view that I want:
| violation_name | Total |
|:-------------:|:--------------:|
|No Parking | 3 |
|Speed Contest | 2 |
|No Helmet | 1 |
I perform this code that joins the violator table and violation:
SELECT *,count(violators.violation_id) as vid
FROM violators
LEFT JOIN violation ON violation.violation_id = violators.violation_id
LEFT JOIN previous_violator ON previous_violator.violator_id = violators.violator_id
WHERE date_apphrehend BETWEEN '$from' AND '$to'
GROUP BY violators.violation_id
My problem is, I want to join the previous violator table that count to the total based on the violation_name.
You can first union all to get them into a single result and then count(*) it. Finally join with Violation to get names. ie:
select violation_name, count(*) as cnt
from (select violation_id from Violators
union all
select violation_id from previous_Violators) tmp
inner join Violation on tmp.violation_id = Violation.violation_id
group by Violation.violation_id, violation_name;
Sample DBFiddle demo.
PS: Sample is in postgreSQL but it would be the same for most backends. You didn't tag your backend.

In PostgreSQL, conditionally count rows

Background
I'm a novice Postgres user running a local server on a Windows 10 machine. I've got a dataset g that looks like this:
+--+---------+----------------+
|id|treatment|outcome_category|
+--+---------+----------------+
|a |1 |cardiovascular |
|a |0 |cardiovascular |
|b |0 |metabolic |
|b |0 |sensory |
|c |1 |NULL |
|c |0 |cardiovascular |
|c |1 |sensory |
|d |1 |NULL |
|d |0 |cns |
+--+---------+----------------+
The Problem
I'd like to get a count of outcome_category by outcome_category for those id who are "ever treated" -- defined as "id's who have any row where treatment=1".
Here's the desired result:
+----------------+---------+
|outcome_category| count |
+----------------+---------+
|cardiovascular | 3 |
|sensory | 1 |
|cns | 1 |
+----------------+---------+
It would be fine if the result had to contain metabolic, like so:
+----------------+---------+
|outcome_category|treatment|
+----------------+---------+
|cardiovascular | 3 |
|metabolic | 0 |
|sensory | 1 |
|cns | 1 |
+----------------+---------+
Obviously I don't need the rows to be in any particular order, though descending would be nice.
What I've tried
Here's a query I've written:
select treatment, outcome_category, sum(outcome_ct)
from (select max(treatment) as treatment,
outcome_category,
count(outcome_category) as outcome_ct
from g
group by outcome_category) as sub
group by outcome_category, sub.treatment;
But it's a mishmash result:
+---------+----------------+---+
|treatment|outcome_category|sum|
+---------+----------------+---+
|1 |cardiovascular |3 |
|1 |sensory |2 |
|0 |metabolic |1 |
|1 |NULL |0 |
|0 |cns |1 |
+---------+----------------+---+
I'm trying to identify the "ever exposed" id's using that first line in the subquery: select max(treatment) as treatment. But I'm not quite getting at the rest of it.
EDIT
I realized that the toy dataset g I originally gave you above doesn't correspond to the idiosyncrasies of my real dataset. I've updated g to reflect that many id's who are "ever treated" won't have a non-null outcome_category next to a row with treatment=1.
Interesting little problem. You can do:
select
outcome_category,
count(x.id) as count
from g
left join (
select distinct id from g where treatment = 1
) x on x.id = g.id
where outcome_category is not null
group by outcome_category
order by count desc
Result:
outcome_category count
----------------- -----
cardiovascular 3
sensory 1
cns 1
metabolic 0
See running example at db<>fiddle.
This would appear to be just a simple aggregation,
select outcome_category, Count(*) count
from t
where treatment=1
group by outcome_category
order by Count(*) desc
Demo fiddle

In SQL, query a table by transposing column results

Background
Forgive the title of this question, as I'm not really sure how to describe what I'm trying to do.
I have a SQL table, d, that looks like this:
+--+---+------------+------------+
|id|sex|event_type_1|event_type_2|
+--+---+------------+------------+
|a |m |1 |1 |
|b |f |0 |1 |
|c |f |1 |0 |
|d |m |0 |1 |
+--+---+------------+------------+
The Problem
I'm trying to write a query that yields the following summary of counts of event_type_1 and event_type_2 cut (grouped?) by sex:
+-------------+-----+-----+
| | m | f |
+-------------+-----+-----+
|event_type_1 | 1 | 1 |
+-------------+-----+-----+
|event_type_2 | 2 | 1 |
+-------------+-----+-----+
The thing is, this seems to involve some kind of transposition of the 2 event_type columns into rows of the query result that I'm not familiar with as a novice SQL user.
What I've tried
I've so far come up with the following query:
SELECT event_type_1, event_type_2, count(sex)
FROM d
group by event_type_1, event_type_2
But that only gives me this:
+------------+------------+-----+
|event_type_1|event_type_2|count|
+------------+------------+-----+
|1 |1 |1 |
|1 |0 |1 |
|0 |1 |2 |
+------------+------------+-----+
You can use a lateral join to unpivot the data. Then use conditional aggregate to calculate m and f:
select v.which,
count(*) filter (where d.sex = 'm') as m,
count(*) filter (where d.sex = 'f') as f
from d cross join lateral
(values (d.event_type_1, 'event_type_1'),
(d.event_type_2, 'event_type_2')
) v(val, which)
where v.val = 1
group by v.which;
Here is a db<>fiddle.

Is it possible to include a sub-query inside a select statement?

I have the following three tables:
dbo_CURRENCYRATES
+-------------+---------+---------------+
|CURRENCY_ID |BUY_RATE |DATE_EFFECTIVE |
+-------------+---------+---------------+
|GBP |1.5 |01/01/2000 |
|USD |2.5 |01/01/2000 |
|EUR |0.5 |01/01/2000 |
|GBP |1.7 |01/01/2017 |
|USD |2.7 |01/01/2017 |
|EUR |0.7 |01/01/2017 |
+-------------+---------+---------------+
dbo_DISCOUNTRATES
+-------------+--------------+
|DISCOUNT |DISCOUNT_RATE |
+-------------+--------------+
|50 |0.5 |
|25 |0.25 |
|35 |0.35 |
+-------------+--------------+
dbo_CUSTOMER
+-------+---------------+---------+----------+
|ID |NAME |CURRENCY | DISCOUNT |
+-------+---------------+---------+----------+
|1 |Widgets INC |USD |50 |
|2 |Widgets GMBH |EUR |35 |
|3 |Widgets PLC |GBP |25 |
+-------+---------------+---------+----------+
and a query
CurrentExchangeRate
+--------------------------------------------+
SELECT
a.CURRENCY_ID
,a.BUY_RATE
FROM dbo_CURRENCYRATES AS t
INNER JOIN
(
SELECT
CURRENCY_ID
,MAX(DATE_EFFECTIVE) AS MaxDate
FROM dbo_CURRENCYRATES
GROUP BY dbo_CURRENCYRATES.CURRENCY_ID
) AS tm
ON (a.CURRENCY_ID = b.CURRENCY_ID)
AND (a.DATE_EFFECTIVE = b.MaxDate);
I have a select query which selects data from dbo_CUSTOMER which includes currency and discount.
SELECT
dbo_CUSTOMER.ID
,dbo_CUSTOMER.NAME
,dbo_CUSTOMER.CURRENCY_ID AS [CURRENCY]
,BUY_RATE
,dbo_CUSTOMER.DISCOUNT
,DISCOUNT_RATE
FROM dbo_CUSTOMER
WHERE ID = '1';
Is is possible to include in this query, a sub-query which lets me get the BUY_RATE for the currency (from a separate query) and the the appropriate discount rate by querying another table?
I have a separate query to get the currency rate, as I need to have the latest value for the specific currency.
So, in pseudo code, can I do this:
SELECT
dbo_CUSTOMER.ID
,dbo_CUSTOMER.NAME
,dbo_CUSTOMER.CURRENCY_ID AS [CURRENCY]
,dbo_CUSTOMER.DISCOUNT
,(
SELECT BUY_RATE
FROM CurrentExchangeRate
WHERE CurrentExchangeRate.CURRENCY_ID = [CURRENCY]
) as BUY_RATE
,(
SELECT DISCOUNT_RATE
FROM dbo_DISCOUNTRATES
WHERE dbo_DISCOUNTRATES.DISCOUNT = dbo_CUSTOMER.DISCOUNT
) as DISCOUNT_RATE
FROM dbo_CUSTOMER
WHERE ID = '1';
Is is possible to do this, firstly in a query and secondly in an access database?
Thanks
Yes, you can. Only tip you need to know in your sub queries, for what you need, is that you need to select with a TOP 1, and ORDER BY DESC on the date field.

Join 2 Lookup Tables to a Detail table

I have 3 tables:
Products
Groups
Sales
The products table contains the following information:
|**Product ID**|**Product Description**|
|--------------|-----------------------|
|1 |Wine |
|2 |Ruler |
|3 |Gas |
|4 |Water |
The Groups table contains the following information:
|**Group ID**|**Group Description**|
|------------|---------------------|
|1 |Cheetahs |
|2 |Elephants |
|3 |Cougars |
The Sales table contains the following information:
|**GroupID**|**Product ID**|**Amount Sold**|**Day Sold**|
|-----------|--------------|---------------|------------|
|1 |2 | 3|07-31-2016 |
|1 |1 | 1|07-31-2016 |
|2 |3 | 5|07-31-2016 |
|1 |4 | 2|08-01-2016 |
Now I have to produce a query that could bring me a result set as follows (with the condition that I want only results from 07-31-2016):
|**Group ID**|**Product ID**|**Amount Sold**|
|------------|--------------|---------------|
|1 |1 |1 |
|1 |2 |3 |
|1 |3 |0 |
|1 |4 |0 |
|2 |1 |0 |
|2 |2 |0 |
|2 |3 |5 |
|2 |4 |0 |
|3 |1 |0 |
|3 |2 |0 |
|3 |3 |0 |
|3 |4 |0 |
I thought this was going to be just a matter of using left joins, but it appears it wouldn't bring me back the result I was looking for (I don't want to omit products nor groups which weren't sold).
So in summary, I need to display all groups and all products no matter if they had an appearance in the Sales table.
I would appreciate any feedback on this matter, directions on where to look at or any logic that I may be missing!
EDIT
I've marked Matt's (big thanks) post as the answer, turns out I've never used a cross join.
I only added the where clause inside the left join of the Sales table in order to get just the sales made on 07-31-2016
SELECT
g.GroupId
,p.ProductId
,SUM(COALESCE(s.AmountSold,0)) as AmountSold
FROM
Products p
CROSS JOIN Groups g
LEFT JOIN Sales s
ON p.ProductId = s.ProductId
AND g.GroupId = s.GroupId
AND daySold = '07-31-2016'
GROUP BY
g.GroupId
,p.ProductId
ORDER BY
g.GroupId
,p.ProductId
SELECT
g.GroupId
,p.ProductId
,SUM(COALESCE(s.AmountSold,0)) as AmountSold
FROM
Products p
CROSS JOIN Groups g
LEFT JOIN Sales s
ON p.ProductId = s.ProductId
AND g.GroupId = s.GroupId
AND s.daySold = '07-31-2016'
GROUP BY
g.GroupId
,p.ProductId
ORDER BY
g.GroupId
,p.ProductId
Note your expected results you provided are wrong for group 1 product 4 there were 2 of those in the sale.
You could join all the Products with all the Groups (so you get a list of all the combinations of the two) and then add the additional information (filtering out the results based on your condition with a WHERE statement.
SELECT A.[Group ID]
, B.[Product ID]
, ISNULL([Amount Sold], 0) AS 'Amount Sold'
FROM Groups A
INNER JOIN Products B
ON 1 = 1
LEFT JOIN Sales C
ON C.[Group ID] = A.[Group ID]
AND C.[Product ID] = B.[Product ID]
WHERE [Day Sold] = '07-31-2016'