I would like to sum the values inside my internal table by name, article, date (year) and price, but the problem is that my collect statement is not working. I think this has something to do with my date value which is of YYYY-MM-DD, therefore the collect statement makes a difference between 2014-10-12 and 2014-11-12 and inserts them as two different values.
How can I change the collect statement, so that it knows "2014-10-12" is the same as "2014-11-12".
My table
Hans - Mouse - 80 - 2014-12-01
Hans - Mouse - 80 - 2014-05-01
Albert - Keyboard - 50 - 2015-05-04
Albert - Keyboard - 80 - 2015-10-06
Albert - Keybaoard - 100 - 2016-01-01
What I want
Hans - Mouse - 160 - 2014
Albert - Keyboard - 130 - 2015
Albert - Keybaoard - 100 - 2016
My Code
SELECT * FROM gv_table INTO CORRESPONDING FIELDS OF wa_table
WHERE date(4) BETWEEN '20140101' AND '20160101' <-- date(4) is not working. It gives me an error
COLLECT wa_table INTO lt_table.
ENDSELECT.
Or do I have to loop a second time thru my lt_table and do another collect?
EDIT
I have a global table:
Hans - Mouse - 60 - 2014-12-02
Hans - Mouse - 50 - 2014-12-02
Peter - Keyboard - 40 - 2014-03-02
What I want my local table to look like:
Hans - Mouse - 60 - 2014
Hans - Mouse - 50 - 2014
Peter - Keyboard - 40 - 2014
And then aggregate it:
Hans - Mouse 110 - 2014
Peter - Keyboard - 40 - 2014
The COLLECT statement is unfortunately not very flexible. It will consider the primary key of your internal table as keys to aggregate on. If you didn't declare an explicit primary key when you declared your table, then that means all non-numeric fields. And there is no way to tell it to perform any transformations on the keys or values before aggregating.
If you insist on using the COLLECT statement, then a possible workaround is:
Create a separate type for your sum structure with only the fields you want. When you want your date represented by only the year, create a separate field of type GJAHR (or your preferred TYPE n LENGTH 4)
Create a table and a work-area of that type
In your loop, write the date to your new 4-digit date of the work area (wa_sum-year = wa_table-date(4).) You can likely fill the rest with MOVE-CORRESPONDING.
COLLECT your output work-area into your output table.
But a more modern solution could be to create a sum table using the constuctor expressions which were added in SAP_BASIS 7.40.
And from SAP_BASIS 7.50(?) on you can also use the LEFT aggretate function in your SQL statement to already truncate the date on the database. And if you do that, you can even let the database do the aggregation by using GROUP BY:
SELECT name, product, SUM( quantity ) AS sum, LEFT( date, 4) AS year
FROM dbtab
INTO CORRESPONDING FIELDS OF #wa_table
WHERE date BETWEEN '20140101' AND '20160101'
GROUP BY name, product, LEFT( date, 4 ).
Related
I have a list of time-intervals (in seconds) between consecutive datetime-stamped records in a dataset in Access 2010. I want to find the median time interval for each Animal on each Date.
Please can someone tell me how to go about this - either in SQL or VBA?
Example data:
Animal Date Time_interval
1 18/07/14 1
1 18/07/14 18
1 18/07/14 100
1 18/07/14 121
1 18/07/14 156
2 18/07/14 14
2 18/07/14 35
(I also have a field for Time, not included here to keep things simple)
Thanks very much!!
You could run a query with to compare the 2 date/time entries using the DateDiff function.
Here is the setup for DateDiff:
DateDiff ( interval, date1, date2, [firstdayofweek], [firstweekofyear])
From what I understand, create a new query and add a field like this:
median_time_interval: DateDiff ("s", Date, Time)
=================
ID - Date - Note
3 - 1/1/2014 - happy
3 - 2/1/2014 - mad
3 - 3/1/2014 - sad
4 - 1/1/2014 - mad
4 - 2/1/2014 - happy
=================
Would like to return the latest date per ID as well as associated Note. Results would look like this:
=================
ID - Date - Note
3 - 3/1/2014 - sad
4 - 2/1/2014 - happy
=================
I can group by ID and then select the max(Date). However, I can't get the associated Note as no aggregate function is applicable. I just want: "the note associated with the Id/date I selected via the max function."
I don't know the official or proper or efficient way to do it. It feels like i'm hacking it by rejoining the aggregate query back into the original data set. Any help would be greatly appreciated as i constantly run into this issue.
One easy way is to wrap the max query in a subselect:
select
m.id, m.datecolumn, m.note
from
(select max(datecolumn) datecolumn, id
from mytable
group by id) sub
inner join mytable m on m.id = sub.id and m.datecolumn= sub.datecolumn
The problem I'm facing is probably easy to fix, but I can't seem to find an answer online due to the specificity of the issue.
In my database, I have a 3 tables to denote how an educational course is planned. Suppose there is a course called Working with Excel. This means the table Courses has a row for this.
The second table denotes cycles of the same course. If the course is given on Jan 1 2013 and Feb 1 2013, in the underlying tables Cycles, you will find 2 rows, one for each date.
I currently already have an SQL script that gives me two columns: The course name, and a comma separated list with all the Cycle dates.
Please note I am using dd/MM/yyyy notation
This is how it's currently set up (small excerpt, this is the SELECT statement to explain the desired output):
SELECT course.name,
stuff((SELECT distinct ',' + CONVERT(varchar(10), cycleDate, 103) --code 101 = mm/dd/yyyy, code 103 = dd/mm/yyyy
FROM cycles t2
where t2.courseID= course.ID and t2.cycleDate > GETDATE()
FOR XML PATH('')),1,1,'') as 'datums'
The output it gives me:
NAME DATUMS
---------------------------------------------------
Working with Excel 01/01/2013,01/02/2013
Some other course 12/3/2013, 1/4/2013, 1/6/2013
The problem is that I need to add info from the third table I haven't mentioned yet. The table ExtraDays contains additional days for a cycle, in case this spans more than a day.
E.g., if the Working with Excel course takes 3 days, (Jan 1+2+3 and Feb 1+2+3), each of the course cycles will have 2 ExtraDays rows that contain the 'extra days'.
The tables would look like this:
Table COURSES
ID NAME
---------------------------------------------------
1 Working with Excel
Table CYCLES
ID DATE COURSEID
---------------------------------------------------
1 1/1/2013 1
2 1/2/2013 1
Table EXTRADAYS
ID EXTRADATE CYCLEID
---------------------------------------------------
1 2/1/2013 1
2 3/1/2013 1
3 2/2/2013 2
4 3/2/2013 2
I need to add these ExtraDates to the comma-separated list of dates in my output. Preferably sorted, but this is not necessary.
I've been stumped quite some time by this. I have some SQL experience, but apparently not enough for this issue :)
I'm hoping to get the following output:
NAME DATUMS
--------------------------------------------------------------------------------------
Working with Excel 01/01/2013,02/01/2013,03/01,2013,01/02/2013,02/02/2013,03/02/2013
I'm well aware that the database structure could be improved to simplify this, but unfortunately this is a legacy application, I cannot change the structure.
Can anyone point me in the right way to combining these two columns.
I hope I described my issue clear enough for you. Else, just ask :)
SELECT course.name,
stuff((SELECT distinct ',' + CONVERT(varchar(10), cycleDate, 103) --code 101 = mm/dd/yyyy, code 103 = dd/mm/yyyy
FROM (select id, date, courseid from cycles
union
select id, extradate, courseid from extradays) t2
where t2.courseID= course.ID and t2.cycleDate > GETDATE()
FOR XML PATH('')),1,1,'') as 'datums'
I have the following table in MSaccess and i need to group by joining 2 columns, and give a count of the duplicated, within a certain date/time range, and if possible type and city, but that isn't mandatory.
LocationX LocationY DateTimeStamp, City Type
100 150 08/01/2013 8:59:44 Brisbane RadioJob
101 155 08/01/2013 9:56:01 Brisbane RadioJob
100 150 08/01/2013 8:49:39 Brisbane RadioJob
103 150 08/01/2013 8:55:13 Brisbane RadioJob
i need to join column 0 and column 1 together and do a count, but only selecting locations within a certain time range.
so for example, if i chose between 08/01/2013 8:49:00 and 08/01/2013 8:59:59 I should get the following table:
LocationX LocationY CountOfLocation City Type
100 150 2 Brisbane RadioJob
103 150 1 Brisbane RadioJob
As it stands ive written 2 queries in MSaccess with design view to get this to work, but i would like to try and learn how to do this in one SQL statement.
Thanks in advance,
Mike
SELECT LocationX, LocationY, City, Type, COUNT(*) CountOfLocation
FROM tableName
WHERE DateTimeStamp BETWEEN '2013-08-01 8:49:00' AND '2013-08-01 8:59:59'
GROUP BY LocationX, LocationY, City, Type
SQLFiddle Demo Link
UPDATE
SELECT LocationX, LocationY, City, Type, COUNT(*) AS CountOfLocation
FROM tableName
WHERE DateTimeStamp BETWEEN #2013-08-01 08:49:00# AND #2013-08-01 08:59:59#
GROUP BY LocationX, LocationY, City, Type
SELECT
L.LocationX,
L.LocationY,
Count(*) AS CountOfLocation,
L.City,
L.Type
FROM
Locations L
WHERE
DateTimeStamp >= #08/01/2013 08:49:00#
AND DateTimeStamp < #08/01/2013 09:00:00#
GROUP BY
L.LocationX,
L.LocationY,
L.City,
L.Type;
I'd like to point out that my change from using BETWEEN to inequalities was quite deliberate. The reason is that using BETWEEN requires that the underlying date data type has a particular resolution or granularity (here, seconds). But let's say your database grows and gets upsized to SQL Server some day. After addressing obvious issues like date literals, there's going to be lurking a nasty little gotcha in a conversion to the datetime data type: suddenly values between seconds will be possible and data will be improperly excluded.
My professional advice is to begin consistently using the syntax as show in my query in ALL your database queries: an inclusive start time and exclusive end time. Consider that learning best-practice habits will serve you well in any DBMS...
Also, while it's true that the date literal in Access requires the # wrapper, you have the option of choosing SQL-Server compatible syntax at the database level which then works with '. I actually recommend this switch as the syntax is less quirky, and in the upsizing scenario will be a gigantic blessing due to having far, far less work to do. But if you change now, your existing queries will all have to be fixed immediately.
If I have a table containing schedule information that implies particular dates, is there a SQL statement that can be written to convert that information into actual rows, using some sort of CROSS JOIN, perhaps?
Consider a payment schedule table with these columns:
StartDate - the date the schedule begins (1st payment is due on this date)
Term - the length in months of the schedule
Frequency - the number of months between recurrences
PaymentAmt - the payment amount :-)
SchedID StartDate Term Frequency PaymentAmt
-------------------------------------------------
1 05-Jan-2003 48 12 1000.00
2 20-Dec-2008 42 6 25.00
Is there a single SQL statement to allow me to go from the above to the following?
Running
SchedID Payment Due Expected
Num Date Total
--------------------------------------
1 1 05-Jan-2003 1000.00
1 2 05-Jan-2004 2000.00
1 3 05-Jan-2005 3000.00
1 4 05-Jan-2006 4000.00
1 5 05-Jan-2007 5000.00
2 1 20-Dec-2008 25.00
2 2 20-Jun-2009 50.00
2 3 20-Dec-2009 75.00
2 4 20-Jun-2010 100.00
2 5 20-Dec-2010 125.00
2 6 20-Jun-2011 150.00
2 7 20-Dec-2011 175.00
I'm using MS SQL Server 2005 (no hope for an upgrade soon) and I can already do this using a table variable and while loop, but it seemed like some sort of CROSS JOIN would apply but I don't know how that might work.
Your thoughts are appreciated.
EDIT: I'm actually using SQL Server 2005 though I initially said 2000. We aren't quite as backwards as I thought. Sorry.
I cannot test the code right now, so take it with a pinch of salt, but I think that something looking more or less like the following should answer the question:
with q(SchedId, PaymentNum, DueDate, RunningExpectedTotal) as
(select SchedId,
1 as PaymentNum,
StartDate as DueDate,
PaymentAmt as RunningExpectedTotal
from PaymentScheduleTable
union all
select q.SchedId,
1 + q.PaymentNum as PaymentNum,
DATEADD(month, s.Frequency, q.DueDate) as DueDate,
q.RunningExpectedTotal + s.PaymentAmt as RunningExpectedTotal
from q
inner join PaymentScheduleTable s
on s.SchedId = q.SchedId
where q.PaymentNum <= s.Term / s.Frequency)
select *
from q
order by SchedId, PaymentNum
Try using a table of integers (or better this: http://www.sql-server-helper.com/functions/integer-table.aspx) and a little date math, e..g. start + int * freq
I've used table-valued functions to achieve a similar result. Basically the same as using a table variable I know, but I remember being really pleased with the design.
The usage ends up reading very well, in my opinion:
/* assumes #startdate and #enddate schedule limits */
SELECT
p.paymentid,
ps.paymentnum,
ps.duedate,
ps.ret
FROM
payment p,
dbo.FUNC_get_payment_schedule(p.paymentid, #startdate, #enddate) ps
ORDER BY p.paymentid, ps.paymentnum
A typical solution is to use a Calendar table. You can expand it to fit your own needs, but it would look something like:
CREATE TABLE Calendar
(
calendar_date DATETIME NOT NULL,
is_holiday BIT NOT NULL DEFAULT(0),
CONSTRAINT PK_Calendar PRIMARY KEY CLUSTERED calendar_date
)
In addition to the is_holiday you can add other columns that are relevant for you. You can write a script to populate the table up through the next 10 or 100 or 1000 years and you should be all set. It makes queries like that one that you're trying to do much simpler and can give you additional functionality.