If condition TRUE in a row (that is grouped) - sql

Table:
|Months |ID|Commission|
|2020-01|1 |2312 |
|2020-02|2 |24412 |
|2020-02|1 |123 |
|... |..|... |
What I need:
COUNT(Months),
ID,
SUM(Commission),
Country
GROUP BY ID...
How it should look:
|Months |ID|Commission|
|4 |1 |5356 |
|6 |2 |5436 |
|... |..|... |
So I want to know how many months each ID received his commission, however (and that's the part where I ask for your help) if the ID is still receiving commission up to this month (current month) - I want to exclude him from the list. If he stopped receiving comm last month or last year, I want to see him in the table.
In other words, I want a table with old clients (who doesn't receive commission anymore)

Use aggregation. Assuming there is one row per month:
select id, count(*)
from t
group by id
having max(months) < date_format(now(), '%Y-%m');
Note this uses MySQL syntax, which was one of the original tags.

Related

Oracle SQL - How to return the name with the highest ID ending in a certain number

I have a table structured like this where I need to get the ID's last number, how many people's ID ends with that number, and the person with the highest ID:
Members: |ID |Name |
-----------------
|123 |foo |
|456 |bar |
|789 |boo |
|1226|far |
The result I need to get looks something like this
|LAST_NUMBER |OCCURENCES |HIGHEST_ID_GUY |
---------------------------------------------
|3 |1 |foo |
|6 |2 |far |
|9 |1 |boo |
However, while I can get the first two results to display correctly, I have no idea how to display HIGHEST_ID_GUY. My code looks like this:
SELECT DISTINCT SUBSTR(id, LENGTH(id - 1), LENGTH(id)) AS LAST_NUMBER,
COUNT(*) AS OCCURENCES
/* This is where I need to add HIGHEST_ID_GUY */
FROM Members
GROUP BY SUBSTR(id, LENGTH(id - 1), LENGTH(id))
ORDER BY LAST_NUMBER
Any help appreciated :)
If id is a number, then use arithmetic operations:
select mod(id, 10) as last_digit,
count(*),
max(name) keep (dense_rank first order by id desc) as name_at_biggest
from t
group by mod(id, 10);
If id is a string, then you need to convert to a number or something similar to define the "highest id". For instance:
select substr(id, -1) as last_digit,
count(*),
max(name) keep (dense_rank first order by to_number(id) desc) as name_at_biggest
from t
group by substr(id, -1);

Aggregate query in MS-Access that calculates current and cumulative amounts by account is exceptionally slow

I'm having trouble with an Access query. Although it runs , it is exceptionally slow; and I fear I may be overlooking a simpler, more elegant solution in my query design.
For context, I work in an accounts receivable office. We have thousands of customers, and each customer can have one or more accounts. Every month, transactions post to the various accounts, and I am preparing the invoices for the customers. In my particular case, a customer's first invoice is always 001, then 002, and so on. We bill monthly.
To describe a simplified example, in month of January 2020, customer A may have the following transactions in the Transaction table:
+-----------------------------+
|TransID|Account|Amount|InvNum|
+-----------------------------+
|1 |1 |$10.00|001 |
|2 |2 |$5.00 |001 |
|3 |3 |$2.00 |001 |
+-----------------------------+
So, in the above example, I would want to issue invoice 001 to customer A for a total of $17.00, broken out by account. The invoice would look something like this:
+-----------------------+
|Account|Current|ToDate |
|1 |$10.00 |$10.00 |
|2 |$5.00 |$5.00 |
|3 |$2.00 |$2.00 |
+-----------------------+
$17.00 $17.00
Now, suppose that in February 2020, additional transactions post. A simplified version of the Transaction table would look like this:
+-----------------------------+
|TransID|Account|Amount|InvNum|
+-----------------------------+
|1 |1 |$10.00|001 |
|2 |2 |$5.00 |001 |
|3 |3 |$2.00 |001 |
|4 |1 |$3.00 |002 |
|5 |3 |$4.00 |002 |
+-----------------------------+
Invoice #002 issued to customer A would need to look something like this:
+-----------------------+
|Account|Current|ToDate |
|1 |$3.00 |$13.00 |
|2 |$0.00 |$5.00 |
|3 |$4.00 |$6.00 |
+-----------------------+
$7.00 $24.00
The query I'm having trouble with is specifically designed to capture the month's activity by account and to calculate a cumulative total for the "ToDate" column on the invoice. The challenge is that not every account will have transactions in a given month. Note that account 2 did not post any transactions in February. So invoice 002 has to show a current amount of $0.00 for account 2, but it also needs to know the cumulative amount ($5.00 + $0.00 = $5.00) for account 2.
The problematic query is itself made up of a few subqueries:
BillNumByAcccountQ: an aggregate query that selects and groups all accounts by invoice number.
CurrentQ: Also an aggregate query that selects and sums all the transaction amounts (from the transaction table), which is left-joined to BillNumByAccountQ. The left-join is necessary to ensure that there is a row for every bill number. The "Current" field in this query is given by the expression Sum(Nz(Amount,0)). The result set of this query contains over 20K rows.
Finally, the problematic query is defined by the following SQL statement:
SELECT
Q1.Account
,Q1.InvNum
,Q1.CURRENT
,(
SELECT SUM(CURRENT)
FROM CurrentQ
WHERE Q1.Account = Account
AND Q1.InvNum >= InvNum
) AS ToDate
FROM CurrentQ AS Q1;
This query runs and runs and runs, and it eventually causes Access to stop responding. I do not even know how many rows it has because it never finishes running. I fear that I'm overlooking a way simpler solution.
Apologies for so much information, and I appreciate any advice on simplifying this.
Generally, doing a sub-query inside a select statement is slow, since it often needs to run the sub-query for every single row of the main query.
Doing the aggregation all at once is likely going to be faster:
SELECT
Q1.Account
,Q1.InvNum
,Q1.CURRENT
,SUM(i.CURRENT) AS ToDate
FROM CurrentQ AS Q1
JOIN CurrentQ AS i
ON i.Account = Q1.Account
AND i.InvNum >= Q1.InvNum
GROUP BY Q1.Account, Q1.InvNum, Q1.Current;
In addition, if you're able to edit the database, you'd probably want to add indexes for the Account and InvNum columns.

How can I summarize data by year in SQL?

I'm sure the request is rather straight-forward, but I'm stuck. I'd like to take the first table below and turn it into the second table by summing up Incremental_Inventory by Year.
+-------------+-----------+----------------------+-----+
|Warehouse_ID |Date |Incremental_Inventory |Year |
+-------------+-----------+----------------------+-----+
| 1|03/01/2010 |125 |2010 |
| 1|08/01/2010 |025 |2010 |
| 1|02/01/2011 |150 |2011 |
| 1|03/01/2011 |200 |2011 |
| 2|03/01/2012 |125 |2012 |
| 2|03/01/2012 |025 |2012 |
+-------------+-----------+----------------------+-----+
to
+-------------+-----------+---------------------------+
|Warehouse_ID |Date |Cumulative_Yearly_Inventory|
+-------------+-----------+---------------------------+
| 1|03/01/2010 |125 |
| 1|08/01/2010 |150 |
| 1|02/01/2011 |150 |
| 1|03/01/2011 |350 |
| 2|03/01/2012 |125 |
| 2|03/01/2012 |150 |
+-------------+-----------+---------------------------+
If your DBMS, which you haven't told us, supports window functions you could simply do something like:
SELECT warehouse_id,
date,
sum(incremental_inventory) OVER (PARTITION BY warehouse_id,
year(date)
ORDER BY date) cumulative_yearly_inventory
FROM elbat
ORDER BY date;
year() maybe needs to replaced by the means your DBMS provides to extract the year from a date.
If it doesn't support window functions you had to use a subquery and aggregation.
SELECT t1.warehouse_id,
t1.date,
(SELECT sum(t2.incremental_inventory)
FROM elbat t2
WHERE t2.warehouse_id = t1.warehouse_id
AND year(t2.date) = year(t1.date)
AND t2.date <= t1.date) cumulative_yearly_inventory
FROM elbat t1
ORDER BY t1.date;
However, if there are two equal dates, this will print the same sum for both of them. One would need another, distinct column to sort that out and as far as I can see you don't have such a column in the table.
I'm not sure if you want the sum over all warehouses or only per warehouse. If you don't want the sums split by warehouses but one sum for all warehouses together, remove the respective expressions from the PARTITION BY or inner WHERE clause.
If you have SAS/ETS then the time series tasks will do this for you. Assuming not, here's a data step solution.
Use RETAIN to hold value across rows
Use BY to identify the first record for each year
data want;
set have;
by year;
retain cum_total;
if first.year then cum_total=incremental_inventory;
else cum_total+incremental_inventory;
run;

Select rows that are duplicates on two columns

I have data in a table. There are 3 columns (ID, Interval, ContactInfo). This table lists all phone contacts. I'm attempting to get a count of phone numbers that called twice on the same day and have no idea how to go about this. I can get duplicate entries for the same number but it does not match on date. The code I have so far is below.
SELECT ContactInfo, COUNT(Interval) AS NumCalls
FROM AllCalls
GROUP BY ContactInfo
HAVING COUNT(AllCalls.ContactInfo) > 1
I'd like to have it return the date, the number of calls on that date if more than 1, and the phone number.
Sample data:
|ID |Interval |ContactInfo|
|--------|------------|-----------|
|1 |3/1/2017 |8009999999 |
|2 |3/1/2017 |8009999999 |
|3 |3/2/2017 |8001234567 |
|4 |3/2/2017 |8009999999 |
|5 |3/3/2017 |8007771111 |
|6 |3/3/2017 |8007771111 |
|--------|------------|-----------|
Expected result:
|Interval |ContactInfo|NumCalls|
|------------|-----------|--------|
|3/1/2017 |8009999999 |2 |
|3/3/2017 |8007771111 |2 |
|------------|-----------|--------|
Just as juergen d suggested, you should try to add Interval in your GROUP BY. Like so:
SELECT AC.ContactInfo
, AC.Interval
, COUNT(*) AS qnty
FROM AllCalls AS AC
GROUP BY AC.ContactInfo
, AC.Interval
HAVING COUNT(*) > 1
The code should like this :
select Interval , ContactInfo, count(ID) AS NumCalls from AllCalls group by Interval, ContactInfo having count(ID)>1;

sum rows from one table and move it to another table

How can I sum rows from one table (based on selected critiria) and move the outcome to another table.
I have a table related to costs within project:
Table "costs":
id| CostName |ID_CostCategory| PlanValue|DoneValue
-------------------------------------------------------
1 | books |1 |100 |120
2 | flowers |1 |90 |90
3 | car |2 |150 |130
4 | gas |2 |50 |45
and I want to put the sum of "DoneValue" of each ID_CostCategory into table "CostCategories"
Table "CostCategories":
id|name |planned|done
------------------------
1 |other|190 |takes the sum from above table
2 |car |200 |takes the sum from above table
Many thanks
I would not store this, because as soon as anything changes in Costs then CostCategories will be out of date, instead I would create a view e.g:
CREATE VIEW CostCategoriesSum
AS
SELECT CostCategories.ID,
CostCategories.Name,
SUM(COALESCE(Costs.PlanValue, 0)) AS Planned,
SUM(COALESCE(Costs.DoneValue, 0)) AS Done
FROM CostCategories
LEFT JOIN Costs
ON Costs.ID_CostCategory = CostCategories.ID
GROUP BY CostCategories.ID, CostCategories.Name;
Now instead of referring to the table, you can refer to the view and the Planned and Done totals will always be up to date.
INSERT INTO CostCategories(id,name,planned,done)
SELECT ID_CostCategory, CostName, SUM(PlanValue), SUM(DoneValue)
FROM costs GROUP BY ID_CostCategory, CostName