SQL Select rows based on date - sql

I have a two tables (Table1 and Table2):
Table1
Date Name Other
2014-02-08 Alex 1
2014-06-15 Bob 1
Table2
Date Name Count
2014-02-07 Alex 1
2014-01-31 Alex 2
2014-02-09 Alex 4
2014-02-08 Alex 10
2014-02-10 Alex 0
2014-02-01 Alex 4
2014-01-08 Alex 5
2014-03-08 Alex 4
2014-06-01 Bob 22
2014-06-02 Bob 0
2014-06-10 Bob 9
2014-06-15 Bob 3
2014-06-16 Bob 3
2014-06-20 Bob 5
2014-06-14 Bob 18
2014-07-11 Bob 1
2014-08-15 Bob 2
I am having a difficult time constructing a query that accomplishes the following:
From Table1, run through each "Date" and "Name"
For a given "Date" and "Name" in Table1, go through Table2 and grab all rows that also have the same "Name" and that have dates that are 10 days before the "Date" (in Table1) and 5 days after "Date" (in Table1).
So, for Table1, "Alex" on "2014-02-08", I want to grab all rows in Table2 that also say "Alex" but whose date is between "2014-01-29" (10 days before 2014-02-08) and "2014-02-13" (5 days after 2014-02-08).
For "Bob" on "2014-06-15", I want to grab all rows in Table2 that also say "Bob" but whose date is between "2014-06-05" (10 days before 2014-06-15) and "2014-06-20" (5 days after 2014-06-15).
The expected output is:
Date Name Count
2014-02-07 Alex 1
2014-01-31 Alex 2
2014-02-09 Alex 4
2014-02-08 Alex 10
2014-02-10 Alex 0
2014-02-01 Alex 4
2014-06-10 Bob 9
2014-06-15 Bob 3
2014-06-16 Bob 3
2014-06-20 Bob 5
2014-06-14 Bob 18
In my real work, the number of rows in Table1 is much larger and the number of days I'd like to grab before/after the reference date can vary.

I think you can do something like this:
select t2.*
from table1 t2
where exists (select 1
from table1 t1
where t1.name = t2.name and t1.date >= t2.date - 'interval 10 day' and
t1.date <= t2.date + 'interval 10 day'
);

See:
http://sqlfiddle.com/#!4/82e1e/10
Assuming Oracle, but you get the picture:-). You would need to format the date in the results. That is left for your perusal.
In case, the above link cannot be opened, the sql is:
select t2.*
from
table2 t2, table1 t1
where
t1.name = t2.name
and t2.date1 > t1.date1 -10
and t2.date1 <= t1.date1 +5;
The result is:
DATE1 NAME COUNT
February, 07 2014 00:00:00+0000 Alex 1
January, 31 2014 00:00:00+0000 Alex 2
February, 09 2014 00:00:00+0000 Alex 4
February, 08 2014 00:00:00+0000 Alex 10
February, 10 2014 00:00:00+0000 Alex 0
February, 01 2014 00:00:00+0000 Alex 4
June, 10 2014 00:00:00+0000 Bob 9
June, 15 2014 00:00:00+0000 Bob 3
June, 16 2014 00:00:00+0000 Bob 3
June, 20 2014 00:00:00+0000 Bob 5
June, 14 2014 00:00:00+0000 Bob 18

Related

Non-repeated values in Big Query

I am fairly new to SQL, so this might be an easy solution for most, but I am having an issue with joins in Big Query. I have two tables:
TABLE A
id name purchases
1 alex 2
2 jane 7
3 peter 8
4 mario 1
5 luigi 6
TABLE B
id name visited
1 alex jan
2 jane jan
2 jane feb
3 peter jan
3 peter feb
3 peter mar
4 mario feb
5 luigi mar
I want my end result to have unique number of purchases per name/id, so the following:
TABLE C
id name visited purchases
1 alex jan 2
2 jane jan 7
2 jane feb 0
3 peter jan 8
3 peter feb 0
3 peter mar 0
4 mario feb 1
5 luigi mar 6
However, no matter what joins I perform, I end up with number of purchases per user matched every time, like the following:
id name visited purchases
1 alex jan 2
2 jane jan 7
2 jane feb 7
3 peter jan 8
3 peter feb 8
3 peter mar 8
4 mario feb 1
5 luigi mar 6
What would be the query to have Table C from Tables A and B?
Thank you.
One method is using row_number()
select b.*, coalesce(a.purchases, 0) purchases
from (
select *, row_number() over(partition by id order by visited) rn
from b ) b
left join a on a.id = b.id and b.rn=1
You may wish to decode visited to an ordinal depending on ordering requirements, for example
.. order by case visited when 'jan' then 1 when .. end ..

How can I calculate daily snapshots of my total sales on SQL?

I have a table (let's call it DiodeSales) that tells me the total number of diode sales I made, grouped by date, diode color, and country. This is a sample of this schema:
Date Color Country Sales
June, 20 2016 00:00:00 Green US 1
June, 20 2016 00:00:00 Red Japan 1
June, 20 2016 00:00:00 Red US 1
June, 21 2016 00:00:00 Red US 1
June, 22 2016 00:00:00 Green US 1
June, 22 2016 00:00:00 Red US 1
June, 23 2016 00:00:00 Green US 1
June, 23 2016 00:00:00 Red Japan 1
June, 23 2016 00:00:00 Red US 1
June, 24 2016 00:00:00 Green US 1
June, 24 2016 00:00:00 Red US 1
I want to be able to have have an additional column that tells me how many diodes we've sold up until that point. So, for example, using the above data, the {June 23, Red, 1, US} row would have a total sales value of 4, because we've sold 4 red diodes in the US at that point.
I initially thought a cumulative sum would do the trick. So I wrote this: (sqlfiddle here)
SELECT
t1.Date,
t1.Color,
t1.Country,
t1.Sales,
SUM(t2.Sales) AS CumulativeSales
FROM DiodeSales AS t1
INNER JOIN DiodeSales AS t2
ON t1.Date >= t2.Date
AND t1.Color = t2.Color
AND t1.Country = t2.Country
GROUP BY
t1.Date,
t1.Color,
t1.Country
This gives me the cumulative sum, as expected, but it does not give me the total sales for a given color in a given country on a given day. In particular, because some specific days may have 0 sales in some country, they will not have a cumulative value associated to it. For example, consider the results of the previous table:
Date Color Country Sales CumulativeSales
June, 20 2016 00:00:00 Green US 1 1
June, 20 2016 00:00:00 Red Japan 1 1
June, 20 2016 00:00:00 Red US 1 1
June, 21 2016 00:00:00 Red US 1 2
June, 22 2016 00:00:00 Green US 1 2
June, 22 2016 00:00:00 Red US 1 3
June, 23 2016 00:00:00 Green US 1 3
June, 23 2016 00:00:00 Red Japan 1 2
June, 23 2016 00:00:00 Red US 1 4
June, 24 2016 00:00:00 Green US 1 4
June, 24 2016 00:00:00 Red US 1 5
If I were to look for the column corresponding to Japan on June 24, I'd find nothing (because there was no Japan sale that day, so there is no Japan row for that day). I don't think there's a way to do this in SQL, but is it possible to populate this resulting table with values on days in which some countries had no sales? The starting table will always have at least one column for each day for some country.
I am aware I could just write a simple
SELECT SUM(Sales) FROM DiodeSales
WHERE Date &lt= #someDate AND Color = #someColor AND Country = #someCountry
to get this information, but this is for a table that has to be formatted in that way for it to be used by another piece of already-made software.
EDIT: Someone mentioned this as a potential duplicate of Calculate a Running Total in SQL Server, but that post only addresses efficiency while calculating a running sum. I already have various ways of calculating this sum, but I'm looking for a way to fix the issue of missing day/country combinations for days when there were no sales in that country. For the above example, the fixed query would return this:
Date Color Country Sales CumulativeSales
June, 20 2016 00:00:00 Green US 1 1
June, 20 2016 00:00:00 Red Japan 1 1
June, 20 2016 00:00:00 Red US 1 1
June, 21 2016 00:00:00 Green US 0 1
June, 21 2016 00:00:00 Red Japan 0 1
June, 21 2016 00:00:00 Red US 1 2
June, 22 2016 00:00:00 Green US 1 2
June, 22 2016 00:00:00 Red Japan 0 1
June, 22 2016 00:00:00 Red US 1 3
June, 23 2016 00:00:00 Green US 1 3
June, 23 2016 00:00:00 Red Japan 1 2
June, 23 2016 00:00:00 Red US 1 4
June, 24 2016 00:00:00 Green US 1 4
June, 24 2016 00:00:00 Red Japan 0 2
June, 24 2016 00:00:00 Red US 1 5
Try this:
SELECT [Date], Color, Country, Sales,
SUM(Sales) OVER(PARTITION BY Color, Country ORDER BY [Date] rows unbounded preceding) as RunningTotal
FROM YourTable
ORDER BY [Date], Color
It produces the output as expected.
[EDIT]
If you're looking for solution for missing dates, countries and colors, try this (replace #tmp with the name of your table):
SELECT A.[Date], A.Color, A.Country, COALESCE(B.Sales, 0) AS Sales
, SUM(COALESCE(B.Sales, 0)) OVER(PARTITION BY A.Color, A.Country ORDER BY A.[Date] rows unbounded preceding) as RunningTotal
FROM (
SELECT [Date], Color, Country
FROM (SELECT DISTINCT [Date] FROM #tmp) AS q1 CROSS JOIN
(SELECT DISTINCT Color FROM #tmp) AS q2 CROSS JOIN
(SELECT DISTINCT Country FROM #tmp) AS q3
) AS A
LEFT JOIN #tmp AS B ON A.[Date] = B.[Date] AND A.Color= B.Color AND A.Country = B.Country
ORDER BY A.[Date], A.Color
Above query produces:
Date Color Country Sales RunningTotal
2016-06-20 Green Japan 0 0
2016-06-20 Green US 1 1
2016-06-20 Red Japan 1 1
2016-06-20 Red US 1 1
2016-06-21 Green US 0 1
2016-06-21 Green Japan 0 0
2016-06-21 Red US 1 2
2016-06-21 Red Japan 0 1
2016-06-22 Green Japan 0 0
2016-06-22 Green US 1 2
2016-06-22 Red Japan 0 1
2016-06-22 Red US 1 3
2016-06-23 Green US 1 3
2016-06-23 Green Japan 0 0
2016-06-23 Red US 1 4
2016-06-23 Red Japan 1 2
2016-06-24 Green Japan 0 0
2016-06-24 Green US 1 4
2016-06-24 Red Japan 0 2
2016-06-24 Red US 1 5
I think you should use left join instead of inner join
SELECT
t.Date,
t.Color,
t.Country,
t.CumulativeSales
from DiodeSales t
left join
(SELECT
t1.Date,
t1.Color,
t1.Country,
t1.Sales,
SUM(t2.Sales) AS CumulativeSales
FROM DiodeSales AS t1
GROUP BY
t1.Date,
t1.Color,
t1.Country) t2
on
t.Date=t2.date
and t.Color=t2.color
and t.Country=t2.country
Try this
Select distinct Date into SalesDate From DiodeSales
SELECT S.Date,t.Color,t.Country,t.CumulativeSales
from DiodeSales t left join
(SELECt t1.Date,t1.Color,t1.Country,t1.Sales,
SUM(t2.Sales) AS CumulativeSales FROM DiodeSales AS t1
GROUP BY
t1.Date,
t1.Color,
t1.Country) t2 on
S.Date=t2.date
and t.Color=t2.color
and t.Country=t2.country
join
SalesDate S
on t.date=S.date

return the last row that meets a condition in sql

I have two tables:
Meter
ID SerialNumber
=======================
1 ABC1
2 ABC2
3 ABC3
4 ABC4
5 ABC5
6 ABC6
RegisterLevelInformation
ID MeterID ReadValue Consumption PreviousReadDate ReadType
============================================================================
1 1 250 250 1 jan 2015 EST
2 1 550 300 1 feb 2015 ACT
3 1 1000 450 1 apr 2015 EST
4 2 350 350 1 jan 2015 EST
5 2 850 500 1 feb 2015 ACT
6 2 1000 150 1 apr 2015 ACT
7 3 1500 1500 1 jan 2015 EST
8 3 2500 1000 1 mar 2015 EST
9 3 5000 2500 4 apr 2015 EST
10 4 250 250 1 jan 2015 EST
11 4 550 300 1 feb 2015 ACT
12 4 1000 450 1 apr 2015 EST
13 5 350 350 1 jan 2015 ACT
14 5 850 500 1 feb 2015 ACT
15 5 1000 150 1 apr 2015 ACT
16 6 1500 1500 1 jan 2015 EST
17 6 2500 1000 1 mar 2015 EST
18 6 5000 2500 4 apr 2015 EST
I am trying to group by meter serial and return the last actual read date for each of the meters but I am unsure as to how to accomplish this. Here is the sql I have thus far:
select a.SerialNumber, ReadTypeCode, MAX(PreviousReadDate) from Meter as a
left join RegisterLevelInformation as b on a.MeterID = b.MeterID
where ReadType = 'ACT'
group by a.SerialNumber,b.ReadTypeCode, PreviousReadDate
order by a.SerialNumber
I can't seem to get the MAX function to take effect in returning only the latest actual reading row and it returns all dates and the same meter serial is displayed several times.
If I use the following sql:
select a.SerialNumber, count(*) from Meter as a
left join RegisterLevelInformation as b on a.MeterID = b.MeterID
group by a.SerialNumber
order by a.SerialNumber
then each serial is shown only once. Any help would be greatly appreciated.
Like #PaulGriffin said in his comment you need to remove PreviousReadDate column from your GROUP BY clause.
Why are you experiencing this behaviour?
Basically the partition you have chosen - (SerialNumber,ReadTypeCode,PreviousReadDate) for each distinct pair of those values prints you SerialNumber, ReadTypeCode, MAX(PreviousReadDate). Since you are applying a MAX() function to each row of the partition that includes this column you are simply using an aggregate function on one value - so the output of MAX() will be equal to the one without it.
What you wanted to achieve
Get MAX value of PreviousReadDate for every pair of (SerialNumber,ReadTypeCode). So this is what your GROUP BY clause should include.
select a.SerialNumber, ReadTypeCode, MAX(PreviousReadDate) from Meter as a
left join RegisterLevelInformation as b on a.MeterID = b.MeterID
where ReadType = 'ACT'
group by a.SerialNumber,b.ReadTypeCode
order by a.SerialNumber
Is the correct SQL query for what you want.
Difference example
ID MeterID ReadValue Consumption PreviousReadDate ReadType
============================================================================
1 1 250 250 1 jan 2015 EST
2 1 550 300 1 feb 2015 ACT
3 1 1000 450 1 apr 2015 EST
Here if you apply the query with grouping by 3 columns you would get result:
SerialNumber | ReadTypeCode | PreviousReadDate
ABC1 | EST | 1 jan 2015 -- which is MAX of 1 value (1 jan 2015)
ABC1 | ACT | 1 feb 2015
ABC1 | EST | 1 apr 2015
But instead when you only group by SerialNumber,ReadTypeCode it would yield result (considering the sample data that I posted):
SerialNumber | ReadTypeCode | PreviousReadDate
ABC1 | EST | 1 apr 2015 -- which is MAX of 2 values (1 jan 2015, 1 apr 2015)
ABC1 | ACT | 1 feb 2015 -- which is MAX of 1 value (because ReadTypeCode is different from the row above
Explanation of your second query
In this query - you are right indeed - each serial is shown only once.
select a.SerialNumber, count(*) from Meter as a
left join RegisterLevelInformation as b on a.MeterID = b.MeterID
group by a.SerialNumber
order by a.SerialNumber
But this query would produce you odd results you don't expect if you add grouping by more columns (which you have done in your first query - try it yourself).
You need to remove PreviousReadDate from your Group By clause.
This is what your query should look like:
select a.SerialNumber, ReadTypeCode, MAX(PreviousReadDate) from Meter as a
left join RegisterLevelInformation as b on a.MeterID = b.MeterID
where ReadType = 'ACT'
group by a.SerialNumber,b.ReadTypeCode
order by a.SerialNumber
To understand how the group by clause works when you mention multiple columns, follow this link: Using group by on multiple columns
You will understand what was wrong with your query and why it returns all dates and the same meter serial is displayed several times.
Good luck!
Kudos! :)

Creating a field(s) that counts days within a month from date range?

Similar to the following:
Count days within a month from date range
I want to find a way, within the MS-Access Query Design environment, to create fields that count the number of month/year days within a date range.
Here is what I want the data to look like:
Row | StartDate | EndDate | #DaysJan2010 | #DaysFeb2010 | #DaysMarch2010
001 01/02/2010 02/04/2012 29 28 31
002 01/02/2010 01/05/2010 4 0 0
003 04/02/2010 05/05/2010 0 0 0
004 01/02/2010 02/04/2012 29 28 31
005 02/02/2012 02/03/2012 0 2 0
Please keep in mind that both month and year are important because I need to be able to distinguish between the number of days that fall within a given date range for January 2010 and January 2011, as opposed to just the number of days within a given date range that are in January.
If there is a systematic way of performing of creating these fields by using SQL in Access, that would be my preferred method.
However, in the event that it is impossible (or very difficult) to do so, I would like to know how to build each field in the expression builder, so that I may at least be able to generate the count fields one at a time.
As always, thank you very much for your time.
There are cases where date manipulations can be aided by a "dates table". Similar to a "numbers table", a "dates table" is a table containing one row for every date in a given range, usually covering the entire range of dates that one could expect to encounter in the actual data.
For sample data in a table named [SampleData]
Row StartDate EndDate
--- ---------- ----------
001 2010-01-02 2012-02-04
002 2010-01-02 2010-01-05
003 2010-04-02 2010-05-05
004 2010-01-02 2012-02-04
005 2012-02-02 2012-02-03
and a [DatesTable] that is simply
theDate
----------
2010-01-01
2010-01-02
2010-01-03
...
2012-12-30
2012-12-31
the query
SELECT
sd.Row,
dt.theDate,
Year(dt.theDate) AS theYear,
Month(dt.theDate) AS theMonth
FROM
SampleData AS sd
INNER JOIN
DatesTable AS dt
ON dt.theDate >= sd.StartDate
AND dt.theDate <= sd.EndDate
returns a row for each date in the interval for each [SampleData].[Row] value. (For this particular sample data, that's 1568 rows in total.)
Performing an aggregation on that
SELECT
Row,
theYear,
theMonth,
COUNT(*) AS NumberOfDays
FROM
(
SELECT
sd.Row,
dt.theDate,
Year(dt.theDate) AS theYear,
Month(dt.theDate) AS theMonth
FROM
SampleData AS sd
INNER JOIN
DatesTable AS dt
ON dt.theDate >= sd.StartDate
AND dt.theDate <= sd.EndDate
) AS allDates
GROUP BY
Row,
theYear,
theMonth
gives us all of the counts
Row theYear theMonth NumberOfDays
--- ------- -------- ------------
001 2010 1 30
001 2010 2 28
001 2010 3 31
001 2010 4 30
001 2010 5 31
001 2010 6 30
001 2010 7 31
001 2010 8 31
001 2010 9 30
001 2010 10 31
001 2010 11 30
001 2010 12 31
001 2011 1 31
001 2011 2 28
001 2011 3 31
001 2011 4 30
001 2011 5 31
001 2011 6 30
001 2011 7 31
001 2011 8 31
001 2011 9 30
001 2011 10 31
001 2011 11 30
001 2011 12 31
001 2012 1 31
001 2012 2 4
002 2010 1 4
003 2010 4 29
003 2010 5 5
004 2010 1 30
004 2010 2 28
004 2010 3 31
004 2010 4 30
004 2010 5 31
004 2010 6 30
004 2010 7 31
004 2010 8 31
004 2010 9 30
004 2010 10 31
004 2010 11 30
004 2010 12 31
004 2011 1 31
004 2011 2 28
004 2011 3 31
004 2011 4 30
004 2011 5 31
004 2011 6 30
004 2011 7 31
004 2011 8 31
004 2011 9 30
004 2011 10 31
004 2011 11 30
004 2011 12 31
004 2012 1 31
004 2012 2 4
005 2012 2 2
We can then report on that, or crosstab it, or do any number of other fun things.
Side note:
One circumstance where a "dates table" can be very useful is when we have to deal with Statutory Holidays. That is because
Sometimes the "day off" for a Statutory Holiday is not the actual day. If "International Bacon Day" falls on a Sunday then we might get the Monday off.
Some Statutory Holidays can be tricky to calculate. For example, Good Friday for us Canadians is (if I remember correctly) "the Friday before the first Sunday after the first full moon after the Spring Equinox".
If we have a "dates table" then we can add a [StatutoryHoliday] Yes/No field to flag all of the (observed) holidays and then use ... WHERE NOT StatutoryHoliday to exclude them.

Populate data from One table to another with out matching Key

Can any one tell me how to do this....
Table 1 Table 2
Cat_ID Cat_Name Term_ID Term_Name
1 ab 1986 January 2013
2 cd 1987 February 2013
3 ef 1988 March 2013
4 gh
I want the Output as :
Table 3
Term_ID Term_Name CAT_ID CAT_Name
1986 January 2013 1 ab
1986 January 2013 2 cd
1986 January 2013 3 ef
1986 January 2013 4 gh
1987 February 2013 1 ab
1987 February 2013 2 cd
1987 February 2013 3 ef
1987 February 2013 4 gh
1988 March 2013 1 ab
1988 March 2013 2 cd
1988 March 2013 3 ef
1988 March 2013 4 gh
I have to write it as a SQL query.
You can use a CROSS JOIN to get the Cartesian result that you want:
select t2.term_id,
t2.term_name,
t1.cat_id,
t1.cat_name
from table1 t1
cross join table2 t2
See SQL Fiddle with Demo. Once you have the result, then you can INSERT the data into table3:
insert into table3 (term_id, term_name, cat_id, cat_name)
select t2.term_id,
t2.term_name,
t1.cat_id,
t1.cat_name
from table1 t1
cross join table2 t2