Displaying same record twice- SQL Reporting Services - sql

Ok, here's the situation: I need to display the same record in two different sections. stupid i know, but here's why.
The Report I am building is grouped by one Field, called Day. Each record has
date/times, an expected arrival date time, and an expected departure date/time.
so, at this point we have something like this:
Day..............Arrival Time..................Departure Time
18/5.............18/5 9.00am.........19/5 11.00am
The boss only wants to show times that relate to the current day in the arrive/depart coloumns (easy enough with expressions), which ends up like this:
Day..............Arrival Time..................Departure Time
18/5..............9.00am.........................-
the next thing he wants is to display the departing time in the correct day 'group', but as you can imagine as soon as you move to the next row, well you move to the next row of the table.
So the question is: is there anyway to display the same record on multiple coloumns? Have i missed something or have i got an unsolvable problem?
NOTE: this is not the only data in my table either. there is (for example) a name coloumn which also needs to be displayed on both days.

Cartesian Joins are great for duplicating data...
DECLARE #ArrDep TABLE
(
Code varchar(1)
)
INSERT INTO #ArrDep (Code) SELECT "A"
INSERT INTO #ArrDep (Code) SELECT "D"
SELECT DateAdd(dd, DateDiff(dd, 0,
CASE
WHEN ad.Code = "A"
THEN mt.ArrivalTime
ELSE mt.DepartureTime
END), 0) as TheDay
, *
FROM MyTable mt, #ArrDep ad
ORDER BY 1

Related

Capture a Value in SQL

I want to capture the last value recorded right before a certain time was recorded. In Healthcare terms I need the max flowsheet value 0-10 that was recorded right before a pain medication was given.
I can add the max(flowsheet recorded time) but I am not sure how to add in the time of the medication so I get the max value that was recorded.
I want to capture the last value recorded right before a certain time was recorded. In Healthcare terms I need the max flowsheet value 0-10 that was recorded right before a pain medication was given.
We know little about your database, so here is the general approach. You want to look at rows before the medication (where mydate < medication_date). Of these rows, you say you to want the maximum flowsheet value (max(flowsheet)).
Furthermore, in your request comments you say the medication_date is in another table.
Putting these things together we get something like:
select max(flowsheet)
from mytable
where mydate < (select medication_date from medication);
Well, the medication table won't really have just one row with the global medication date. So let's assume both tables refer to patients, and you want the information for a particular patient. This would be something like this:
select max(flowsheet)
from mytable t
where patient_id = 12345
and mydate <
(
select medication_date
from medication m
where m.patient_id = t.patient_id
);

How do I create a new SQL table with a percentage column which is conditional on whether information shows up in two other tables?

I have 3 tables, the table called agg((date,sname,open,high,low,close,volume)) contains daily information for every stock for x number of past years. Another table, split(date,sname,post,pre), has info for every time any stock split. Another table, div(date,sname,dividend), has info for every time a stock had a dividend. I want to create a new table, with a column that gives the percent change from closing of the previous day, to the day after, for every stock and every day listed in agg.
Here is the line I have for just the daily change, not including div and split:
create table daily
as
with prevclose as (
select date,sname,close,
lag(close) over (partition by symbol order by date) pclose
from agg
)
select a.*,
100.0*(close - pclose)/(case when pclose=0 then null else pclose end) as prcnt
from prevclose a
where pclose != 0;
I want to change this code to incorporate the change in split and dividends which is not incorporated in the agg table. I don't even need the full calculation for this, but I need help figuring out how to incorporate the condition into the new table. I only need to add in split and div info if there is split and div info for that particular date and time. I think if I could just see the query for a similar problem it would help.

SQL how to implement if and else by checking column value

The table below contains customer reservations. Customers come and make one record in this table, and the last day this table will be updated its checkout_date field by putting that current time.
The Table
Now I need to extract all customers spending nights.
The Query
SELECT reservations.customerid, reservations.roomno, rooms.rate,
reservations.checkin_date, reservations.billed_nights, reservations.status,
DateDiff("d",reservations.checkin_date,Date())+Abs(DateDiff("s",#12/30/1899
14:30:0#,Time())>0) AS Due_nights FROM reservations, rooms WHERE
reservations.roomno=rooms.roomno;
What I need is, if customer has checkout status, due nights will be calculated checkin_date subtracting by checkout date instead current date, also if customer has checkout date no need to add extra absolute value from 14:30.
My current query view is below, also my computer time is 14:39 so it adds 1 to every query.
Since you want to calculate the Due nights upto the checkout date, and if they are still checked in use current date. I would suggest you to use an Immediate If.
The condition to check would be the status of the room. If it is checkout, then use the checkout_date, else use the Now(), something like.
SELECT
reservations.customerid,
reservations.roomno,
rooms.rate,
reservations.checkin_date,
reservations.billed_nights,
reservations.status,
DateDiff("d", checkin_date, IIF(status = 'checkout', checkout_date, Now())) As DueNights
FROM
reservations
INNER JOIN
rooms
ON reservations.roomno = rooms.roomno;
As you might have noticed, I used a JOIN. This is more efficient than merging the two tables with common identifier. Hope this helps !

GROUP BY with date range

I have a table with 4 columns, id, Stream which is text, Duration (int), and Timestamp (datetime). There is a row inserted for every time someone plays a specific audio stream on my website. Stream is the name, and Duration is the time in seconds that they are listening. I am currently using the following query to figure up total listen hours for each week in a year:
SELECT YEARWEEK(`Timestamp`), (SUM(`Duration`)/60/60) FROM logs_main
WHERE `Stream`="asdf" GROUP BY YEARWEEK(`Timestamp`);
This does what I expect... presenting a total of listen time for each week in the year that there is data.
However, I would like to build a query where I have a result row for weeks that there may not be any data. For example, if the 26th week of 2006 has no rows that fall within that week, then I would like the SUM result to be 0.
Is it possible to do this? Maybe via a JOIN over a date range somehow?
The tried an true old school solution is to set up another table with a bunch of date ranges that you can outer join with for the grouping (as in the other table would have all of the weeks in it with a begin / end date).
In this case, you could just get by with a table full of the values from YEARWEEK:
201100
201101
201102
201103
201104
And here is a sketch of a sql statement:
SELECT year_weeks.yearweek , (SUM(`Duration`)/60/60)
FROM year_weeks LEFT OUTER JOIN logs_main
ON year_weeks.yearweek = logs_main.YEARWEEK(`Timestamp`)
WHERE `Stream`="asdf" GROUP BY year_weeks.yearweek;
Here is a suggestion. might not be exactly what you are looking for.
But say you had a simple table with one column [year_week] that contained the values of 1, 2, 3, 4... 52
You could then theoretically:
SELECT
A.year_week,
(SELECT SUM('Duration')/60/00) FROM logs_main WHERE
stream = 'asdf' AND YEARWEEK('TimeStamp') = A.year_week GROUP BY YEARWEEK('TimeStamp'))
FROM
tblYearWeeks A
this obviously needs some tweaking... i've done several similar queries in other projects and this works well enough depending on the situation.
If your looking for a one table/sql based solution then that is deffinately something I would be interested in as well!

SQL - state machine - reporting on historical data based on changeset

I want to record user states and then be able to report historically based on the record of changes we've kept. I'm trying to do this in SQL (using PostgreSQL) and I have a proposed structure for recording user changes like the following.
CREATE TABLE users (
userid SERIAL NOT NULL PRIMARY KEY,
name VARCHAR(40),
status CHAR NOT NULL
);
CREATE TABLE status_log (
logid SERIAL,
userid INTEGER NOT NULL REFERENCES users(userid),
status CHAR NOT NULL,
logcreated TIMESTAMP
);
That's my proposed table structure, based on the data.
For the status field 'a' represents an active user and 's' represents a suspended user,
INSERT INTO status_log (userid, status, logcreated) VALUES (1, 's', '2008-01-01');
INSERT INTO status_log (userid, status, logcreated) VALUES (1, 'a', '2008-02-01');
So this user was suspended on 1st Jan and active again on 1st of February.
If I wanted to get a suspended list of customers on 15th January 2008, then userid 1 should show up. If I get a suspended list of customers on 15th February 2008, then userid 1 should not show up.
1) Is this the best way to structure this data for this kind of query?
2) How do I query the data in either this structure or in your proposed modified structure so that I can simply have a date (say 15th January) and find a list of customers that had an active status on that date in SQL only? Is this a job for SQL?
This can be done, but would be a lot more efficient if you stored the end date of each log. With your model you have to do something like:
select l1.userid
from status_log l1
where l1.status='s'
and l1.logcreated = (select max(l2.logcreated)
from status_log l2
where l2.userid = l1.userid
and l2.logcreated <= date '2008-02-15'
);
With the additional column it woud be more like:
select userid
from status_log
where status='s'
and logcreated <= date '2008-02-15'
and logsuperseded >= date '2008-02-15';
(Apologies for any syntax errors, I don't know Postgresql.)
To address some further issues raised by Phil:
A user might get moved from active, to suspended, to cancelled, to active again. This is a simplified version, in reality, there are even more states and people can be moved directly from one state to another.
This would appear in the table like this:
userid from to status
FRED 2008-01-01 2008-01-31 s
FRED 2008-02-01 2008-02-07 c
FRED 2008-02-08 a
I used a null for the "to" date of the current record. I could have used a future date like 2999-12-31 but null is preferable in some ways.
Additionally, there would be no "end date" for the current status either, so I think this slightly breaks your query?
Yes, my query would have to be re-written as
select userid
from status_log
where status='s'
and logcreated <= date '2008-02-15'
and (logsuperseded is null or logsuperseded >= date '2008-02-15');
A downside of this design is that whenever the user's status changes you have to end date their current status_log as well as create a new one. However, that isn't difficult, and I think the query advantage probably outweighs this.
Does Postgres support analytic queries? This would give the active users on 2008-02-15
select userid
from
(
select logid,
userid,
status,
logcreated,
max(logcreated) over (partition by userid) max_logcreated_by_user
from status_log
where logcreated <= date '2008-02-15'
)
where logcreated = max_logcreated_by_user
and status = 'a'
/
#Tony the "end" date isn't necessarily applicable.
A user might get moved from active, to suspended, to cancelled, to active again. This is a simplified version, in reality, there are even more states and people can be moved directly from one state to another.
Additionally, there would be no "end date" for the current status either, so I think this slightly breaks your query?
#Phil
I like Tony's solution. It seems to most approriately model the situation described. Any particular user has a status for a given period of time (a minute, an hour, a day, etc.), but it is for a duration, not an instant in time. Since you want to know who was active during a certain period of time, modeling the information as a duration seems like the best approach.
I am not sure that additional statuses are a problem. If someone is active, then suspended, then cancelled, then active again, each of those statuses would be applicable for a given duration, would they not? It may be a vey short duration, such as a few seconds or a minute, but they would still be for a length of time.
Are you concerned that a person's status can change multiple times in a given day, but you want to know who was active for a given day? If so, then you just need to more specifically define what it means to be active on a given day. If it is enough that they were active for any part of that day, then Tony's answer works well as is. If they would have to be active for a certain amount of time in a given day, then Tony's solution could be modified to simply determine the length of time (in hours, or minutes, or days), and adding further restrictions in the WHERE clause to retrieve for the proper date, status, and length of time in that status.
As for there being no "end date" for the current status, that is no problem either as long as the end date were nullable. Simply use something like this "WHERE enddate <= '2008-08-15' or enddate is null".