Showing posts with label created. Show all posts
Showing posts with label created. Show all posts

Wednesday, March 21, 2012

Query for "latest version" of a given row

I am maintaining historical versions of a row in a table. The idea is that a row is not really modified but a new row is created with a reference to the original row. (I guess a sort of generational view or row versioning...)

My problem is that I cannot come up with a query that will give me a list of the rows with only the latest version of the row included. My confusion is the self join -- the possibility (or lack of info before-hand) of how many parent/child rows there are from a given original row.)

Given the following table and data:

Code Snippet

CREATE TABLE Orders (
[OrderId] INT IDENTITY (1, 1),
[ClientName] VARCHAR(10),
[OriginalOrderId] INT
)
GO

INSERT INTO Orders ([ClientName], [OriginalOrderId]) VALUES ('Peter', NULL) -- should go in as 1
INSERT INTO Orders ([ClientName], [OriginalOrderId]) VALUES ('Joseph', NULL) -- should go in as 2
INSERT INTO Orders ([ClientName], [OriginalOrderId]) VALUES ('Mary', NULL) -- should go in as 3
INSERT INTO Orders ([ClientName], [OriginalOrderId]) VALUES ('Jane', 2) -- should go in as 4 ("replaces" 2)
INSERT INTO Orders ([ClientName], [OriginalOrderId]) VALUES ('Paul', 4) -- should go in as 5 ("replaces" 4)
GO

SELECT * FROM Orders
-- need a query that will return me rows 1, 3, and 5
GO

DROP TABLE Orders
GO

The query should return me

Peter

Paul

Mary

I think another approach would be to replace the IDENTITY column with just an INT while adding a [Generation] or [Version] column. The join would be a simple self-join that figures out the maximum version value for a given Id. I will use this if I have to but I kinda like the idea of not having to manage the Id values myself.

What you have is fine. You just need to add an ORDER BY clause to the SELECT:

SELECT * FROM Orders
ORDER BY OrderID

or if you want the top 3:

SELECT TOP 3 * FROM Orders

ORDER BY OrderID

Adamus

|||

Ok revisiting this...you want to replace records?

This is done through UPDATE not INSERT.

The solution to your problem is adding a LastModified smalldatetime in conjuntion with the IDENTITY field.

Adamus

|||

Thank you for responding Adamus but you are not reading my post fully.

I am fully aware that UPDATE is for modifying existing records but that is not what I am trying to accomplish. My requirement is that instead of overwriting the existing row with the new columns, I need to insert a new row with the new values with that new row maintaining a "reference" to the original row. What I am looking for is a query that will return that "newest" from the set of chained rows. (It is clearly layed out if you look at the comments in the code snippet.)

|||

To do it with this design would likely require cursors or some other iterative approach. Your second idea is the right way to go as that would be a more set based approach like below:


Code Snippet

CREATE TABLE Orders (
[OrderId] INT IDENTITY (1, 1),
[ClientName] VARCHAR(10),
[ClientID] INT
)

INSERT INTO Orders ([ClientName], [ClientID]) VALUES ('Peter', 1) -- should go in as 1
INSERT INTO Orders ([ClientName], [ClientID]) VALUES ('Joseph', 2) -- should go in as 2
INSERT INTO Orders ([ClientName], [ClientID]) VALUES ('Mary', 3) -- should go in as 3
INSERT INTO Orders ([ClientName], [ClientID]) VALUES ('Jane', 2) -- should go in as 4 ("replaces" 2)
INSERT INTO Orders ([ClientName], [ClientID]) VALUES ('Paul', 2) -- should go in as 5 ("replaces" 4)


SELECT ClientName
FROM
Orders
INNER JOIN
(
SELECT ClientID, MAX(OrderID) AS MaxorderID
FROM Orders
GROUP BY ClientID
) gr
ON Orders.ClientID = gr.ClientID
AND Orders.OrderID = gr.Maxorderid

go

|||

For clarity and organization, this should be accomplished with a dbo.History table.

You should not be trying to accomplish this in a single table. The documentation and query would be a maintenance nightmare.

You shouldn't be trying to re-invent the wheel on this. History tables are common practice.

Adamus

|||

Thanks Dave. I figured that was the way I would have to do it but I like to double check these things. In my case I need to maintain the history order (i.e. Joseph --> Jane --> Paul) so I will get rid of the IDENTITY column and all three of those rows will have the same OrderId value. I will also add a [Generation] column and the set them to 1, 2, 3 respectively (for J-->J-->P).

- Jason

|||

Adamus,

I greatly simplified the issue for purposes of posting. I cannot use a history file in this case because technically the "old" rows are still valid -- just happens that there is a "newer" version available. So in this case they all must be in the same table.

Just curious, how would your suggestion of a history table solve my query question? I would still be in the same boat trying to determine the newest version of a given set of rows. Like David mentioned, you would have to resort to cursors or recursive queries since maintaining the parent-child relationship does not automatically tell you how many levels you need to go down to find the last node.

|||

Jason Callas wrote:

Adamus,

I greatly simplified the issue for purposes of posting. I cannot use a history file in this case because technically the "old" rows are still valid -- just happens that there is a "newer" version available. So in this case they all must be in the same table.

Just curious, how would your suggestion of a history table solve my query question? I would still be in the same boat trying to determine the newest version of a given set of rows. Like David mentioned, you would have to resort to cursors or recursive queries since maintaining the parent-child relationship does not automatically tell you how many levels you need to go down to find the last node.

Jason,

As I have mentioned, it is very common that duplicate records are stored in a history table that houses the duplicate with a timestamp. An Instead Of INSERT trigger is used on the table to check for duplicates and insert duplicates into the history table.

A simply join to the history table will expose the chronology. No cursor is required.

Happy coding,

Adamus

|||

Jason Callas wrote:

Thanks Dave. I figured that was the way I would have to do it but I like to double check these things. In my case I need to maintain the history order (i.e. Joseph --> Jane --> Paul) so I will get rid of the IDENTITY column and all three of those rows will have the same OrderId value. I will also add a [Generation] column and the set them to 1, 2, 3 respectively (for J-->J-->P).

- Jason

(praying to God that I will never have to touch this code)

Adamus

Tuesday, March 20, 2012

query execution failed for dataset

have all my reports on a server, and so far have had no problems giving
users access. They are all located in the Cg Role, i created. Here's the
delima, I added a new user and to this role. The user can view report
manager, view the folders, and view the reports..but when they try to run a
report, they get this error.
query execution failed for dataset "CrashStatisticalYear" (rsError
executingcommand)
She has access to everything, why is she the only one getting this error'
Please Help!It seems like she doesnt have access to a specific table inside that
dataset.
"Tenchy" <Tenchy@.discussions.microsoft.com> wrote in message
news:4088044D-2DD6-4FB1-A9BC-59BED72B1C24@.microsoft.com...
> have all my reports on a server, and so far have had no problems giving
> users access. They are all located in the Cg Role, i created. Here's the
> delima, I added a new user and to this role. The user can view report
> manager, view the folders, and view the reports..but when they try to run
> a
> report, they get this error.
>
> query execution failed for dataset "CrashStatisticalYear" (rsError
> executingcommand)
>
> She has access to everything, why is she the only one getting this error'
>
> Please Help!
>

Query execution failed for data set <name> For more information about this error navigate

I have created and deployed my first report. It renders fine for me and the other database admin. When others attempt to view it, we get the error

Query execution failed for data set 'periods'. (rsErrorExecutingCommand), For more information about this error navigate to the report server on the local server machine, or enable remote errors

Initially, We created a local group on the machine that hosts both the database and webserver and added the individuals to that group. Then, within SRS Report manager, we added that group to the Browswer role of the report.

The error message was slightly different, in that it couldn't even open the Datasource.

We then added an individual to the database as dbreader, and got the above message. It apprently is starting to render, and when it encounters the first query (dataset "periods", which populates a drop down list for a parameter), it chokes. BTW, the Periods dataset executes a stored procedure dbo.Period_List that has no parameters. It returns a list of reporting periods.

I could not figure out how to "enable remote errors" or find an error log on the server. The C:\Program Files\Microsoft SQL Server\MSSQL.3\Reporting Services\LogFiles Log files did not appear to record any errors.

Please advise!

Have you given execute permissions on the stored procedure? You can try troubleshooting with SQL Server Profiler (http://msdn2.microsoft.com/en-us/library/ms187929.aspx), but that might be like hitting a gnat with a sledge hammer.

Larry

|||

I want to add some information. We recently enabled remote errors, and found that EXECUTE permission on the stored procedure was denied.

Does this mean that we have to grant permissions on every stored procedure to User Groups?

I love SRS, but this is turning into an Admin nightmare.

|||

Ok, let's back up a second and take another run at this. As long as you don't require the data on your reports to change based on who is viewing them, use a shared DataSource with stored credentials (http://technet.microsoft.com/en-us/library/ms178308.aspx). That way, you admin a single user and everyone (including you during development) sees the same report.

To continue to use Windows Authentication, yes, you would need to grant permissions for every stored procedure that you want SSRS to use to any user that will run a report using that stored procedure.

Larry

|||

thank you!

Ok, the first option sounds like the way to go for the moment, but something you said intrigued me...

"As long as you don't require the data on your reports to change based on who is viewing them..."

Actually, we would like to restrict what the users see based upon who they are, and haven't figured out how to do that yet. For example, if a person is from business unit A, we would like them only to see their business units data. All business units are in the same table however, so we would have to use a parameter or a filter, based upon the longin User ID. Not sure how to do this, any ideas?

|||

Well, you could implement row level security to filter the results returned based on the user. Here is a tutorial from Books On Line (BOL) (http://msdn2.microsoft.com/en-us/library/ms365305.aspx) and here is a white paper discussing it (http://www.microsoft.com/technet/prodtechnol/sql/2005/multisec.mspx). It is a bit complex to get setup, but the results are well worth the effort.

Larry