MSSQL query to get weekno from date, groupby weekno and sum sales - sql

Is this possible in sql alone?
I have a table which contains the following rows
StoreID | Date | SalesItem |
1 | 2016-08-16 | Book |
2 | 2016-08-16 | Pen |
1 | 2016-08-15 | Pen |
1 | 2016-08-15 | Book |
The results I want would be
Store | Week | Sales
1 | 11 | 30
2 | 11 | 15
I'm using sql server 2008, the data set is much larger than the above example but that's basically what i would want to achieve in sql without processing in PHP afterwards.
What I have so far is
select [DATE], [store], count(store) as total, DATEPART(ww,DATE)
AS weeknum from [contracts] where [DATE] >= DATEADD(month, -12, GetDate())
group by [DATE], [store] order by [DATE] asc

Try the below code
SELECT Store,DATEPART(WW,Date),SUM(Sales) FROM TABLE1
GROUP BY Store, DATEPART(WW,Date)
As per edit of Question
SELECT Store,DATEPART(WW,Date),count(Sales) FROM TABLE1
GROUP BY Store, DATEPART(WW,Date)

Related

SQL Query to apply a command to multiple rows

I am new to SQL and trying to write a statement similar to a 'for loop' in other languages and am stuck. I want to filter out rows of the table where for all of attribute 1, attribute2=attribute3 without using functions.
For example:
| Year | Month | Day|
| 1 | 1 | 1 |
| 1 | 2 | 2 |
| 1 | 4 | 4 |
| 2 | 3 | 4 |
| 2 | 3 | 3 |
| 2 | 4 | 4 |
| 3 | 4 | 4 |
| 3 | 4 | 4 |
| 3 | 4 | 4 |
I would only want the row
| Year | Month | Day|
|:---- |:------:| -----:|
| 3 | 4 | 4 |
because it is the only where month and day are equal for all of the values of year they share.
So far I have
select year, month, day from dates
where month=day
but unsure how to apply the constraint for all of year
-- month/day need to appear in aggregate functions (since they are not in the GROUP BY clause),
-- but the HAVING clause ensure we only have 1 month/day value (per year) here, so MIN/AVG/SUM/... would all work too
SELECT year, MAX(month), MAX(day)
FROM my_table
GROUP BY year
HAVING COUNT(DISTINCT (month, day)) = 1;
year
max
max
3
4
4
View on DB Fiddle
So one way would be
select distinct [year], [month], [day]
from [Table] t
where [month]=[day]
and not exists (
select * from [Table] x
where t.[year]=x.[year] and t.[month] <> x.[month] and t.[day] <> x.[day]
)
And another way would be
select distinct [year], [month], [day] from (
select *,
Lead([month],1) over(partition by [year] order by [month])m2,
Lead([day],1) over(partition by [year] order by [day])d2
from [table]
)x
where [month]=m2 and [day]=d2

SQL - Count number of transactions if at least one of the transactions is today

I have a database of transactions made by customers such that each transaction has a specific date. I need to count the number of transactions made by each customer in the last two months ONLY if there was a transaction made by the customer today.
I have been thinking that it requires me to use WHERE to set the complete range of two months and another HAVING statement to make sure the newest date (MAX of that customers transactions) is equal to today's date but I cannot seem to get it to work. Does this sound like the correct way to be going about this problem or is there a better way?
Thank you!
You don't provide any information about how your schema is, but I assume you have a Customer table and a Transaction table. Consider this example with 4 customers and 12 transactions.
Customers
| id | name |
|----|----------|
| 1 | Google |
| 2 | Facebook |
| 3 | Hooli |
| 4 | Yahoo! |
Transactions
| id | transaction_date | customer_id |
|----|------------------|-------------|
| 1 | 2017-04-15 | 1 |
| 2 | 2017-06-24 | 1 |
| 3 | 2017-07-09 | 1 |
| 4 | 2017-07-24 | 1 |
| 5 | 2017-07-23 | 2 |
| 6 | 2017-07-22 | 2 |
| 7 | 2017-07-21 | 2 |
| 8 | 2017-07-24 | 2 |
| 9 | 2017-07-24 | 3 |
| 10 | 2017-07-23 | 4 |
| 11 | 2017-07-22 | 4 |
| 12 | 2017-07-21 | 4 |
To count the number of transactions the last two months by each customer a simple group by will do the job:
select name, count(*) as number_of_transactions
from transactions t
inner join customers c on c.id = t.customer_id
where t.transaction_date > dateadd(month, -2, getdate())
group by c.name
This yields
| name | number_of_transactions |
|----------|------------------------|
| Facebook | 4 |
| Google | 3 |
| Hooli | 1 |
| Yahoo! | 3 |
To retrieve only customers that have a transaction with a transaction_date equal to today we can use an exists to check if such a row exist.
select name, count(*) as number_of_transactions
from transactions t
inner join customers c on c.id = t.customer_id
where t.transaction_date > dateadd(month, -2, getdate())
and exists(select *
from transactions
where customer_id = t.customer_id
and transaction_date = convert(date, getdate()))
group by c.name
So, if a row in the transaction table that has a transaction_date equal to today and the customer_id is equal to the customer_id from the main query include it in the result. Running that query (given that 24th July is today) gives us this result:
| name | number_of_transactions |
|----------|------------------------|
| Facebook | 4 |
| Google | 3 |
| Hooli | 1 |
Check out this sql fiddle http://sqlfiddle.com/#!6/710c94/13
You can toss a subquery in your WHERE clause to find customers that have had sales today:
SELECT count(*) /*count of transactions*/
FROM transactions
WHERE
/*Transactions in the last two months*/
transaction_date > DATEADD(mm, -2, GETDATE())
/*For customers that have had a sale today*/
customer_number in (SELECT customer_number FROM transactions WHERE transaction_date = GETDATE());
Totally guessing at your table structure, table name, and field names, but this should get you close.
Alternatively, you can try to do a inner join:
SELECT t2.CustomerID,count(*) as TransactionsCount
FROM [Tansaction] t1 INNER JOIN [Tansaction] t2
ON t1.CustomerID= t2.CustomerID
WHERE CONVERT(date,t1.TransactionDateTime) = CONVERT(date,GETDATE())
AND t2.TransactionDateTime>= DATEADD(mm, -2, GETDATE())
GROUP BY t2.CustomerID
First, you would need to get the list of customers that had made a transaction today. I'm assuming you have a 'transactiontable' that contains transaction dates and customer details.
Do a select from this transactiontable using the following method:
Select count of distinct(transactiondate), Customer
from Transactiontable
where transactiondate > dateadd(months,-2, getdate())
and customer in (select customer from transactiontable
`where cast(transactiondate as date) = cast(getdate() as date))

Pull data from 2016 but only if it is not in the previous years

I am not sure if this is possible.... But can I pull data for 01-01-16 - 12/31/16 only if there was no data before then. So if they made a purchase before 01-01-16 I would not want to see any data. Bascially I am trying to find new customers for 2016. So in the example below John would NOT pull any data.
Name | Item # | Qty | Purch Date |
-----------+---------+-----+------------+
John | 12 | 7 | 2016-01-05 |
John | 22 | 14 | 2011-01-06 |
John | 11 | 9 | 2013-02-05 |
In this example it would pull all 3.
Name | Item # | Qty | Purch Date |
-----------+---------+-----+------------+
John | 12 | 7 | 2016-01-05 |
John | 22 | 14 | 2016-01-06 |
John | 11 | 9 | 2016-02-05 |
Standard SQL solution based on a Scalar Subquery:
select * from tab as t1
where purch_date between '2016-01-01' and '2016-12-31' -- purchase in 2016
and not exists -- but no purchase by the same customer in a previous year
( select * from tab as t2
where t1.name = t2.name
and t2.purch_date < '2016-01-01'
)
Use FIRST_VALUE function to get the first purchase date for each name and check if it is in the expected range of dates.
select name,item,qty,purch_date
from (
select t.*,
first_value(purch_date) over(partition by name order by purch_date) as first_purchase_date
from tablename t
) x
where first_purchase_date>='2016-01-01' and first_purchase_date<='2016-12-31'
--add a filter condition for purch_date if needed
I would be inclined to use min() as a window function:
select t.*
from (select t.*, min(purch_date) over (partition by name) as min_purch_date
from t
) t
where min_purchase_date >= '2016-01-01' and min_purchase_date < '2017-01-01';
If you just want new customers in 2016, but don't need the details:
select t.name
from t
group by t.name
having min(purchase_date) >= '2016-01-01' and min(purchase_date) < '2017-01-01';

SQL Days before end of the month

i have got table with transactions, looking like:
+----+--------------+----------------+------+
| ID | OrderDate | DeliveryDate | EUR |
+----+--------------+----------------+------+
| 1 | 2015-02-21 | 2015-02-25 | 100 |
| 2 | 2015-03-01 | 2015-03-14 | 110 |
| 3 | 2015-03-01 | 2015-03-17 | 90 |
| 4 | 2015-03-10 | 2015-03-20 | 250 |
| 5 | 2015-03-31 | 2015-03-31 | 350 |
+----+--------------+----------------+------+
ANd I need to get sum of revenue and number of orders (COUNT of IDs) based on Days before the end of the month when order gets delivered.
SELECT datediff(day, OrderDate, CAST(DATEADD(month, DATEDIFF(month,0,getdate()+1,0)-1) as Date) as DBEOM, SUM(EUR) as Rev, COUNT(ID) as NumberOfOrders
FROM transactions
WHERE MONTH(DeliveryDate) = 3 AND YEAR(DeliveryDate) = 2015
GROUP BY datediff(day, OrderDate, CAST(DATEADD(month, DATEDIFF(month,0,getdate()+1,0)-1) as Date) as DBEOM
ORDER BY 1
The result in this case would be like:
+-----+-----+----------------+
|DBEOM| Rev | NumberOfOrders |
+-----+-----+----------------+
| 0 | 350 | 1 |
| 21 | 250 | 1 |
| 30 | 200 | 2 |
+-----+-----+----------------+
This is done in SQL 2008, so I can't simply use EOMONTH. I have tried, what is above, but i am getting
ERROR -
[Microsoft][ODBC SQL Server Driver][SQL Server]The datediff function
requires 3 argument(s).
Many thanks in advance for advice!
The easiest way I've found get the last day of the month with more primitive functions is to get the first day of the next month and then subtract a day.
I'm not a TSQL guy so this syntax likely won't be correct but you need something more like
DATEADD(day, DATEFROMPARTS(DATEPART(year, DATEADD(month,1,getdate()), DATEPART(month, DATEADD(month,1,getdate()), 1), -1)
Try:
SELECT datediff(day,
OrderDate,
dateadd(DAY,
-1,
dateadd(MONTH,
1,
dateadd(DAY,
1-day(DeliveryDate),
DeliveryDate
)
)
)
) as DBEOM, SUM(EUR) as Rev, COUNT(ID) as NumberOfOrders
FROM t
WHERE MONTH(DeliveryDate) = 3 AND YEAR(DeliveryDate) = 2015
GROUP BY datediff(day,
OrderDate,
dateadd(DAY,
-1,
dateadd(MONTH,
1,
dateadd(DAY,
1-day(DeliveryDate),
DeliveryDate
)
)
)
)
ORDER BY 1
sqlfiddle.com

SQL Server 2008 How can I SELECT rows with Value by GROUP(ing) other column?

Hi guys I'm a beginner at SQL so please bear with me.. :)
My question is as follows.
I got this table:
DateTime ID Year Month Value Cost
-------------------|------|--------|-------|-------|--------|
1-1-2013 00:00:01 | 1 | 2013 | 1 | 30 | 90 |
1-1-2013 00:01:01 | 1 | 2013 | 1 | 0 | 0 |
1-1-2013 00:02:01 | 1 | 2013 | 1 | 1 | 3 |
1-2-2013 00:00:01 | 1 | 2013 | 2 | 2 | 6 |
1-2-2013 00:01:01 | 1 | 2013 | 2 | 3 | 9 |
1-2-2013 00:02:01 | 1 | 2013 | 2 | 4 | 12 |
1-3-2013 00:00:01 | 1 | 2013 | 3 | 5 | 15 |
1-3-2013 00:01:01 | 1 | 2013 | 3 | 6 | 18 |
1-3-2013 00:02:01 | 1 | 2013 | 3 | 7 | 21 |
Now what I'm trying to get is this result
Year Month Value Cost
|--------|-------|-------|--------|
| 2013 | 1 | 1 | 3 |
| 2013 | 2 | 4 | 12 |
| 2013 | 3 | 7 | 21 |
As you can see I'm trying to GROUP BY the [Month] and the [Year] and to get the last [Value] for every [Month].
Now as you can understand from the result I do not try to get the MAX() value from the [Value] column but the last value for every [Month] and that is my issue..
Thanks in advance
PS
I was able to GROUP BY the [Year] and the [Month] but as I understand that when I adding the [Value] column the GROUP BY is not effecting the result, as the SQL need more spcification on the value you what the SQL to get..
Instead of using row_number(), you can also use rank(). Using rank() might give you multiple values within the same year and month, see this post.
Because of this, a group by is added.
SELECT
[Year],
[Month],
[Value],
[Cost]
FROM
(
SELECT
[Year],
[Month],
[Value],
[Cost],
Rank() OVER (PARTITION BY [Year], [Month] ORDER BY [DateTime] DESC) AS [Rank]
FROM [t1]
) AS [sub]
WHERE [Rank] = 1
GROUP BY
[Year],
[Month],
[Value],
[Cost]
ORDER BY
[Year] ASC,
[Month] ASC
As stated in the comments, this might still return multiple records for a single month. Therefor the ORDER BY statement can be extended, based on the desired functionality:
Rank() OVER (PARTITION BY [Year], [Month] ORDER BY [DateTime] DESC, [Value] DESC, [Cost] ASC) AS [Rank]
Switching the order of [Value] and [Cost] or ASC <> DESC will influence the rank and because of that the result.
Since you are using SQL Server 2008, you can use row_number() to get the result:
select year, month, value, cost
from
(
select year, month, value, cost,
row_number() over(partition by year, month order by datetime desc) rn
from yourtable
) src
where rn = 1
See SQL Fiddle with Demo
Or you can use a subquery to get this (note: with this version if you have more than one record with the same max datetime per month then you will return each record:
select t1.year, t1.month, t1.value, t1.cost
from yourtable t1
inner join
(
select max(datetime) datetime
from yourtable
group by year, month
) t2
on t1.datetime = t2.datetime
See SQL Fiddle with Demo
Both give the same result:
| YEAR | MONTH | VALUE | COST |
-------------------------------
| 2013 | 1 | 1 | 3 |
| 2013 | 2 | 4 | 12 |
| 2013 | 3 | 7 | 21 |