I'm new in SQL. I was practicing and came on this. How would I retrieve the total number of sales during the first, second, third and fourth quarter separately. I know I'm not providing anything to work from, i would just like to understand the logic and the functions that can be used to do it.
You would use SUM() to give you a total aggregate of the dollar amount of sales, and COUNT(*) for the total count of sales. As far as breaking it down further (such as by quarter), you need to provide more information as far as how your data is structured.
You're not specifying the RDBMS you're using, nor any table structure, so I can only provide some general advice.
First, you need to get both the year and the quarter for each record in your table. As far as I know, every RDBMS has functions that can help you extract the year and the month of a date. Assuming a column named dt that holds the date, you can do something like this:
select year(dt) as y, month(dt) as month, sales
from your_table
That's just a piece of the solution. You need to get the quarter; again, as far as I know, there are functions that can help you. In MS Access you have Iif(), and in other RDBMS (like MySQL) you may have IF() and/ or CASE ... END, so you can get what you need with something like this:
select year(dt) as y
, (case
when month(dt) <= 3 then 1
when month(dt) <= 6 then 2
when month(dt) <= 9 then 3
else 4
end) as q
, sales
from your_table
(As an exercise, do the same thing using If()).
Finally, you can aggregate the data with GROUP BY and the appropriate aggregate functions:
select year(dt) as y
, (case
when month(dt) <= 3 then 1
when month(dt) <= 6 then 2
when month(dt) <= 9 then 3
else 4
end) as q
, sum(sales) as sum_sales
, count(sales) as count_sales
from your_table
group by y, q
You should be able to use something like this to simplify. The DATEPART() function can be found on Microsoft's website here.
SELECT SUM(Sales) AS Quarter1Sales
FROM YourTable
WHERE DATEPART(qq, DateSold) = 1
Note: you will change the 1 to a 2, then 3, then 4 for each quarter. Also change sales to the correct column name and YourTable to your table name.
Related
Using Microsoft SQL Server, I'm trying to get the average days it took someone to complete a transaction in a given month.
Each user has hundreds of transactions so I'm looking for a way to get the count on how many transactions for each person and then their average for the month. I also need to make sure that I remove any NULL returns and convert any negatives to a Zero but keep it accounted for.
Example would look like (Max | 300 | 12.5) for (Person | Transactions | Average).
I've been able to get as far as:
SELECT
[Transaction],
[NAME],
DATEDIFF (d, [Startdate], [Closedate]) AS Days
FROM
[Table]
WHERE
YEAR ([Startdate]) = 2021
AND MONTH ([Closedate]) = 11
AND Closedate IS NOT NULL
I've tried to figure out how to incorporate a CASE statement but it's not working when I tried to do it before the DATEDIFF.
Looks like you can just do a simple GROUP BY with conditional aggregation.
To avoid repeating the DATEDIFF calculation you can stuff it into a CROSS APPLY (VALUES.
Always use date intervals such as >= AND < rather than using functions on date columns
SELECT
t.NAME,
SUM(CASE WHEN v.Days > 0 THEN v.Days ELSE 0 END) AS TotalDays
FROM
[Table] t
CROSS APPLY (VALUES(
DATEDIFF(day, t.Startdate, t.Closedate)
)) v(Days)
WHERE
t.Startdate >= '20211101'
AND t.Startdate < '20211201'
AND t.Closedate IS NOT NULL
GROUP BY
t.NAME;
I have a table similar to below where the same account has its fiscal years (FY) and deductions for each year broken out in multiple rows. Accounts can range from 1 - 20+ years. How do I group to one unique row that shows the current year and how many years its been since the account had a deduction?
from this:
to this:
Started to utilize the CTE approach as I have in the past, but as before it started to get ugly and I know there has to be a simpler approach...
Assuming the current year is the most recent year, you would use aggregation:
select account, max(fy),
sum(case when fy = max_fy then deductions end) as this_year_deduction,
max(fy) - max(case when deduction < 0 then fy end) as years_since_deduction
from (select t.*, max(fy) over (partition by account) as max_fy
from t
) t
group by account;
Note: I assume the third column is the most recent deduction. The query uses a window function to extract that.
Haven't used the methods below but I think it is close to what is needed. Corrections welcome. (Code not tested)
with nonZeroes as
(
select * from YourTable where deductions <> 0
)
select Account,
FY,
FY - LAST_VALUE(FY) OVER (PARTITION BY Account
ORDER BY Year Desc
RANGE BETWEEN CURRENT ROW AND UNBOUNDED PRECEDING) AS years_since_deductions
from nonZeroes
I have a table with just two columns: User_ID and fail_date. Each time somebody's card is rejected they are logged in the table, their card is automatically tried again 3 days later, and if they fail again, another entry is added to the table. I am trying to write a query that counts unique failures by month so I only want to count the first entry, not the 3 day retries, if they exist. My data set looks like this
user_id fail_date
222 01/01
222 01/04
555 02/15
777 03/31
777 04/02
222 10/11
so my desired output would be something like this:
month unique_fails
jan 1
feb 1
march 1
april 0
oct 1
I'll be running this in Vertica, but I'm not so much looking for perfect syntax in replies. Just help around how to approach this problem as I can't really think of a way to make it work. Thanks!
You could use lag() to get the previous timestamp per user. If the current and the previous timestamp are less than or exactly three days apart, it's a follow up. Mark the row as such. Then you can filter to exclude the follow ups.
It might look something like:
SELECT month,
count(*) unique_fails
FROM (SELECT month(fail_date) month,
CASE
WHEN datediff(day,
lag(fail_date) OVER (PARTITION BY user_id,
ORDER BY fail_date),
fail_date) <= 3 THEN
1
ELSE
0
END follow_up
FROM elbat) x
WHERE follow_up = 0
GROUP BY month;
I'm not so sure about the exact syntax in Vertica, so it might need some adaptions. I also don't know, if fail_date actually is some date/time type variant or just a string. If it's just a string the date/time specific functions may not work on it and have to be replaced or the string has to be converted prior passing it to the functions.
If the data spans several years you might also want to include the year additionally to the month to keep months from different years apart. In the inner SELECT add a column year(fail_date) year and add year to the list of columns and the GROUP BY of the outer SELECT.
You can add a flag about whether this is a "unique_fail" by doing:
select t.*,
(case when lag(fail_date) over (partition by user_id order by fail_date) > fail_date - 3
then 0 else 1
end) as first_failure_flag
from t;
Then, you want to count this flag by month:
select to_char(fail_date, 'Mon'), -- should aways include the year
sum(first_failure_flag)
from (select t.*,
(case when lag(fail_date) over (partition by user_id order by fail_date) > fail_date - 3
then 0 else 1
end) as first_failure_flag
from t
) t
group by to_char(fail_date, 'Mon')
order by min(fail_date)
In a Derived Table, determine the previous fail_date (prev_fail_date), for a specific user_id and fail_date, using a Correlated subquery.
Using the derived table dt, Count the failure, if the difference of number of days between current fail_date and prev_fail_date is greater than 3.
DateDiff() function alongside with If() function is used to determine the cases, which are not repeated tries.
To Group By this result on Month, you can use MONTH function.
But then, the data can be from multiple years, so you need to separate them out yearwise as well, so you can do a multi-level group by, using YEAR function as well.
Try the following (in MySQL) - you can get idea for other RDBMS as well:
SELECT YEAR(dt.fail_date) AS year_fail_date,
MONTH(dt.fail_date) AS month_fail_date,
COUNT( IF(DATEDIFF(dt.fail_date, dt.prev_fail_date) > 3, user_id, NULL) ) AS unique_fails
FROM (
SELECT
t1.user_id,
t1.fail_date,
(
SELECT t2.fail_date
FROM your_table AS t2
WHERE t2.user_id = t1.user_id
AND t2.fail_date < t1.fail_date
ORDER BY t2.fail_date DESC
LIMIT 1
) AS prev_fail_date
FROM your_table AS t1
) AS dt
GROUP BY
year_fail_date,
month_fail_date
ORDER BY
year_fail_date ASC,
month_fail_date ASC
I am using SQL Server 2017 and through asking numerous questions on here I have discovered case statements which act as if - else in SQL. This is good but will not satisfy what I need from my result set. If I have a sales table with an amount, date of sale and item description. I am trying to write something like this.
Select
sum(amount) -- total amount,
count(date_of_sale) -- number of days selling
sum(amount where date_of_sale between certain date and certain date)
I don't want to put a where clause outside this because I don't want it to effect the result of the other columns. I can't get around this using a case statement to what I have tried
We can use conditional aggregation here, and sum a CASE expression which includes in the sum only amounts from your date range of interest.
SELECT
SUM(amount) AS total_sales,
COUNT(date_of_sale) AS total_items,
SUM(CASE WHEN date_of_sale BETWEEN start_date AND end_date
THEN amount ELSE 0 END) AS partial_sales,
COUNT(CASE WHEN date_of_sale BETWEEN start_date AND end_date
THEN 1 END) AS partial_items
FROM yourTable;
I want to query statistics using SQL from 3 different days (in a row). The display would be something like:
15 users created today, 10 yesterday, 12 two days ago
The SQL would be something like (for today):
SELECT Count(*) FROM Users WHERE created_date >= '2012-05-11'
And then I would do 2 more queries for yesterday and the day before.
So in total I'm doing 3 queries against the entire database. The format for created_date is 2012-05-11 05:24:11 (date & time).
Is there a more efficient SQL way to do this, say in one query?
For specifics, I'm using PHP and SQLite (so the PDO extension).
The result should be 3 different numbers (one for each day).
Any chance someone could show some performance numbers in comparison?
You can use GROUP BY:
SELECT Count(*), created_date FROM Users GROUP BY created_date
That will give you a list of dates with the number of records found on that date. You can add criteria for created_date using a normal WHERE clause.
Edit: based on your edit:
SELECT Count(*), created_date FROM Users WHERE created_date>='2012-05-09' GROUP BY date(created_date)
The best solution is to use GROUP BY DAY(created_date). Here is your query:
SELECT DATE(created_date), count(*)
FROM users
WHERE created_date > CURRENT_DATE - INTERVAL 3 DAY
GROUP BY DAY(created_date)
This would work I believe though I have no way to test it:
SELECT
(SELECT Count(*) FROM Users WHERE created_date >= '2012-05-11') as today,
(SELECT Count(*) FROM Users WHERE created_date >= '2012-05-10') as yesterday,
(SELECT Count(*) FROM Users WHERE created_date >= '2012-05-11') as day_before
;
Use GROUP BY like jeroen suggested, but if you're planning for other periods you can also set ranges like this:
SELECT SUM(IF(created_date BETWEEN '2012-05-01' AND NOW(), 1, 0)) AS `this_month`,
SUM(IF(created_date = '2012-05-09', 1, 0)) AS `2_days_ago`
FROM ...
As noted below, SQLite doesn't have IF function but there is CASE instead. So this way it should work:
SELECT SUM(CASE WHEN created_date BETWEEN '2012-05-01' AND NOW() THEN 1 ELSE 0 END) AS `this_month`,
SUM(CASE created_date WHEN '2012-05-09' THEN 1 ELSE 0 END) AS `2_days_ago`
FROM ...