Create a nested grid with a self referencing table - c#

I have a table with following schema:
SELECT [ParkingCardId],
[ParentParkingCardId],
[CompanyId],
[DateRequested],
[StaffNo],
[Name],
[Section],
[JobTitle],
[Position],
[Telephone],
[Mobile],
[Fax],
[POBox],
[Email],
[Nationality],
[Gender],
[ShiftType],
[Amount],
[PassIssueDate],
[PassExpiryDate]
FROM [DCAServices].[dbo].[ParkingCards];
The first two columns are key here: ParkingCardId is the PK and ParentParkingCardId, if not NULL, is pointing to another ParkingCardId in the same table and is, itself, a renew of a lost card case.
I want to display this information as hierarchical in Grid on MVC (Kendo) but my DAL is pure Entity Framework based and I have not used any Stored Procedures so far. I know it may not be easy to have a LINQ query to transform this data.
There is also a possibility that there may be generations of parents. A staff might be on his eights renewal. So the parent row on the grid may expand to show one child that may be a parent itself and so on.
I'm currently looking at search result that comes up against query like "Parent-Child relation in the same table"

This SQL will return hierarchical result with Level of current item
WITH cte as
(
SELECT i.[ParkingCardId], i.[ParentParkingCardId], i.[CompanyId], i.[DateRequested], i.[StaffNo], i.[Name], i.[Section], i.[JobTitle], i.[Position],
i.[Telephone], i.[Mobile], i.[Fax], i.[POBox], i.[Email], i.[Nationality], i.[Gender], i.[ShiftType], i.[Amount], i.[PassIssueDate], i.[PassExpiryDate], 0 AS [Level]
FROM [DCAServices].[dbo].[ParkingCards] i
WHERE i.[ParentParkingCardId] is null
UNION ALL
SELECT i1.[ParkingCardId], i1.[ParentParkingCardId], i1.[CompanyId], i1.[DateRequested], i1.[StaffNo], i1.[Name], i1.[Section], i1.[JobTitle], i1.[Position],
i1.[Telephone], i1.[Mobile], i1.[Fax], i1.[POBox], i1.[Email], i1.[Nationality], i1.[Gender], i1.[ShiftType], i1.[Amount], i1.[PassIssueDate], i1.[PassExpiryDate], [Level] + 1
FROM [DCAServices].[dbo].[ParkingCards] i1
INNER JOIN cte
ON cte.[ParkingCardId] = i1.[ParentParkingCardId]
)
SELECT * From cte
ORDER BY [Level]
Check this link Common Table Expressions

Related

C# Sql CTE Query - Select out extra information

I am trying to write a CTE Recursive Query to build a tree relationship of a flat table with a 'marketGroupID' (that element's ID) and a 'parentGroupID' (the element's parent ID). Where each 'marketGroup' can have any number of children 'marketGroups' and so on.
Here is my working query (tested in Sql Server Management):
With cte As
(SELECT [marketGroupID]
,[parentGroupID]
,[marketGroupName]
,[description]
,[iconID]
,[hasTypes]
, 0 As Level
FROM [Eve_Retribution_1.0.7].[dbo].[invMarketGroups]
WHERE [parentGroupID] IS NULL
UNION All
Select mg.marketGroupID
,mg.parentGroupID
,mg.marketGroupName
,mg.description
,mg.iconID
,mg.hasTypes
,c.Level + 1 As Level
FROM [Eve_Retribution_1.0.7].dbo.invMarketGroups mg
Inner Join cte c On mg.parentGroupID = c.marketGroupID
WHERE mg.marketGroupID <> mg.parentGroupID
)
SELECT marketGroupID
,parentGroupID
,marketGroupName
,description
,iconID
,hasTypes
, Level
FROM cte
This Query correctly lists the Elements in the correct order and the Level parameter is meant to be used to build the tree from the elements.
Translating this into C# is where I have a problem. I have integrated this database and all the corresponding tables have been built from my database into my code automatically. I try to call this query with C# as follows:
EveOnlineClassesDataContext context = new EveOnlineClassesDataContext();
IEnumerable<invMarketGroup> results = context.ExecuteQuery<invMarketGroup>
(#"**ABOVE QUERY**");
Where the 'invMarketGroup' class is the automatically created class built by the O/R Designer. My problem is that I lose access to the Level parameter for each 'marketGroup' as it was not part of the table itself and has no element in the provided class.
I want to retrieve from the query the actual 'invMarketGroup' class objects and the level corresponding to each so I can build a tree from this in memory representing this structure. How would I go about doing this?
Thanks
Might be easier to create a View vwInvMarketGroup inside your database using that query:
CREATE VIEW vwInvMarketGroup AS
With cte As
(SELECT [marketGroupID]
,[parentGroupID]
,[marketGroupName]
,[description]
,[iconID]
,[hasTypes]
, 0 As Level
FROM [Eve_Retribution_1.0.7].[dbo].[invMarketGroups]
WHERE [parentGroupID] IS NULL
UNION All
Select mg.marketGroupID
,mg.parentGroupID
,mg.marketGroupName
,mg.description
,mg.iconID
,mg.hasTypes
,c.Level + 1 As Level
FROM [Eve_Retribution_1.0.7].dbo.invMarketGroups mg
Inner Join cte c On mg.parentGroupID = c.marketGroupID
WHERE mg.marketGroupID <> mg.parentGroupID
)
SELECT marketGroupID
,parentGroupID
,marketGroupName
,description
,iconID
,hasTypes
, Level
FROM cte
GO
Then you can use this:
IEnumerable<invMarketGroup> results = context.ExecuteQuery<invMarketGroup>(#"SELECT * FROM vwInvMarketGroup");

Select distinct values ignoring one field

I have a table similar to this
Table(Id int, PropertyId int, UserId int, AccesedOn datetime)
So the table can have value for one property for one user, but different times.
Eg.
1,100,5, 2012-10-16 16:24:48
2,100,5, 2012-10-17 11:22:00
3,100,5, 2012-10-18 17:10:05
What I am trying here is to select distinct properties for specific user, but order by most recent access time.
Also I am joining this table to three other tables which result in providing duplicate values. Therefore I have to use DISTINCT command.
Problem I have is since I doing the orderby AccesedOn, it need to appear on select statement which doesn’t brings the distinct values since the AccesedOn column have different values.
For above example it should return only 3,100,5, 2012-10-18 17:10:05
Any suggestions to overcome this?
;WITH CTE as(
select *,ROW_NUMBER() over (partition by PropertyId,UserId order by AccesedOn desc) as rn
from table1)
select * from CTE where rn=1
It is more likely that you need a subselect than that you need a distinct for this.
select *
from Table A
where AccessedOn in
(Select max(AccessedOn) from table B where A.UserId = B.UserId)
Instead of using DISTINCT, you can use MAX() and group by the rest of the columns
Select Id, PropertyId, UserId, MAX(AccesedOn)
From Table t
group by Id, PropertyId, UserId
This should give you the results you are looking for.

querying flags recursively in database

I have a hierarchy of Article, and each Article has a property IsCommentable. This can take a value of true, false, or NULL. If it is NULL, it means that it inherits the value based on it's parents. Articles can be nested recursively, and there is no limit to the 'depth'.
Now, I need to make a query where I get all the articles from a database which are commentable. Is there any way these can be loaded via an SQL query?
Assuming you are using SQL Server, You can do it with a recursive CTE.
WITH cte (id, iscommentable) AS (
SELECT id, iscommentable FROM Article WHERE iscommentable IS NOT NULL
UNION ALL
SELECT a1.id, a2.iscommentable FROM Article a1
INNER JOIN cte a2 ON a1.parent=a2.id
WHERE a1.iscommentable IS NULL
)
SELECT * FROM cte
SQL fiddle example.

C# and SQL Server: updating trouble with stored procedures

Using:
Microsoft SQL Server 2008
Microsoft Visual Studio 2010
C#
.NET 4.0
WinForms
Ok this is my stored procedure. Brace yourself, it's rushed because I have a deadline of 24 hrs and I was told yesterday as I was leaving work (yes I was very annoyed).
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[MyPareto]
#pgParam varchar(255)
AS
SELECT i.pg,
dbo.OldParetoAnalysis.Pareto,
i.part,
i.sales6months,
a.LostSales6Months,
dbo.NewParetoAnalysis.Pareto
FROM
OPENQUERY(SACBAUTO, 'SELECT dbo.iLines.Part,
dbo.iLines.Pg,
SUM(dbo.iLines.Qty) as sales6months,
dbo.iLines.Prefix
FROM Autopart.dbo.iLines
where prefix = ''i''
and [datetime] > dateadd(month, -6, getdate())
group by
dbo.ilines.pg,
dbo.ilines.part,
dbo.ilines.prefix
order by sales6months desc') i
RIGHT JOIN
dbo.OldParetoAnalysis
on
i.part collate SQL_Latin1_General_CP1_CI_AS = dbo.OldParetoAnalysis.Part
INNER JOIN
dbo.NewParetoAnalysis
ON
dbo.OldParetoAnalysis.Part collate SQL_Latin1_General_CP1_CI_AS = dbo.NewParetoAnalysis.Part
LEFT JOIN
OPENQUERY(SACBAUTO, 'SELECT dbo.aLines.Part,
dbo.aLines.Pg,
SUM(dbo.aLines.Qty) as LostSales6Months,
dbo.aLines.Prefix
FROM Autopart.dbo.aLines
where prefix = ''d''
and [datetime] > dateadd(month, -6, getdate())
group by
dbo.alines.pg,
dbo.alines.part,
dbo.alines.prefix
order by LostSales6Months desc') a
ON
dbo.NewParetoAnalysis.Part collate SQL_Latin1_General_CP1_CI_AS = a.part
/*FULL OUTER JOIN
dbo.NewParetoAnalysis
ON
a.part collate SQL_Latin1_General_CP1_CI_AS = dbo.NewParetoAnalysis.Part*/
WHERE
i.pg = #pgParam
GROUP BY
i.pg,
dbo.OldParetoAnalysis.Pareto,
i.part,
i.sales6months,
a.LostSales6Months,
dbo.NewParetoAnalysis.Pareto
ORDER BY
dbo.OldParetoAnalysis.Pareto asc
Procedure works great, pretty fast too (no idea how hehe). The problem I now have is how to update.
I only want to update 2 tables: OldPareto and NewPareto.
The data will only come from one column which is the NewPareto column. This will update the column in both tables.
Normally its just a simple UPDATE call in the code using the SqlDataAdapter and DataSet.
But since I have this crazy stored procedure I'm troubled as to how this works.
Any C# code you need let me know.
Many thanks!
A long winded way would be too get the new pareto into a new table in
my code, then use the table to update my 2 sql tables
Or in sql procedure itself. I still do not understand what are your objects and their relationships, but here is general way to perform similar updates:
First declare temporary table at the beginning of procedure:
declare #tmpTable table
(
pg int,
oldPareto int,
part int,
sales6months int,
LostSales6Months int,
newPareto int
)
obviously, column types do not match yours so you will need to type them properly.
Perform insert into using your select:
insert into #tmpTable
(pg, oldPareto, part, sales6months, LostSales6Months, newPareto)
select ...
And then perform updates:
update oldPareto
set oldPareto = a.oldPareto,
newPareto = a.newPareto
from oldPareto
inner join #tmpTable a
on ... -- However you join oldPareto table with results of query
update newPareto
set oldPareto = a.oldPareto,
newPareto = a.newPareto
from newPareto
inner join #tmpTable a
on ... -- However you join newPareto table with results of query
Write a PL/SQL code below the "dbo.OldParetoAnalysis.Pareto asc"
eg: UPDATE OldPareto,NewPareto SET OldPareto.NewPareto = 'value' ...
refer this :-
[http://www.java2s.com/Tutorial/MySQL/0140__Insert-Update-Delete/Updatetwotablesinoneupdatestatement.htm]

Adding a Column Programmatically to a SQL Server database

I've taken over an ASP.NET application that needs to be re-written. The core functionality of this application that I need to replicate modifies a SQL Server database that is accessed via ODBC from third party software.
The third-party application creates files that represent printer labels, generated by a user. These label files directly reference an ODBC source's fields. Each row of the table represents a product that populates the label's fields. (So, within these files are direct references to the column names of the table.)
The ASP.NET application allows the user to create/update the data for these fields that are referenced by the labels, by adding or editing a particular row representing a product.
It also allows the occasional addition of new fields... where it actually creates a new column in the core table that is referenced by the labels.
My concern: I've never programmatically altered an existing table's columns before. The existing application seems to handle this functionality fine, but before I blindly do the same thing in my new application, I'd like to know what sort of pitfalls exist in doing this, if any... and if there are any obvious alternatives.
It can become problem when too many columns are added to tables, and you have to be careful if performance is a consideration (covering indexes are not applicable, so expensive bookmark lookups might be performed).
The other alternative is a Key-Value Pair structure: Key Value Pairs in Database design, but that too has it's pitfalls and you are better off creating new columns, as you are suggesting. (KVPs are good for settings)
One option I think is to use a KVP table for storing dynamic "columns" (as first mentioned by Mitch), join the products table with the KVP table based on the product id then pivot the results in order to have all the dynamic columns in the resultset.
EDIT: something along these lines:
Prepare:
create table Product(ProductID nvarchar(50))
insert Product values('Product1')
insert Product values('Product2')
insert Product values('Product3')
create table ProductKVP(ProductID nvarchar(50), [Key] nvarchar(50), [Value] nvarchar(255))
insert ProductKVP values('Product1', 'Key2', 'Value12')
insert ProductKVP values('Product2', 'Key1', 'Value21')
insert ProductKVP values('Product2', 'Key2', 'Value22')
insert ProductKVP values('Product2', 'Key3', 'Value23')
insert ProductKVP values('Product3', 'Key4', 'Value34')
Retrieve:
declare #forClause nvarchar(max),
#sql nvarchar(max)
select #forClause = isnull(#forClause + ',', '') + '[' + [Key] + ']' from (
select distinct [Key] from ProductKVP /* WHERE CLAUSE */
) t
set #forClause = 'for [Key] in (' + #forClause + ')'
set #sql = '
select * from (
select
ProductID, [Key], [Value]
from (
select k.* from
Product p
inner join ProductKVP k on (p.ProductID = k.ProductID)
/* WHERE CLAUSE */
) sq
) t pivot (
max([Value])' +
#forClause + '
) pvt'
exec(#sql)
Results:
ProductID Key1 Key2 Key3 Key4
----------- --------- --------- --------- -------
Product1 NULL Value12 NULL NULL
Product2 Value21 Value22 Value23 NULL
Product3 NULL NULL NULL Value34
It very much depends on the queries you want to run against those tables. The main disadvantage of KVP is that more complex queries can become very inefficient.
A "hybrid" approach of both might be interesting.
Store the values you want to query in dedicated columns and leave the rest in an XML blob (MS SQL has nice features to even query inside the XML) or alternatively in a KVP bag. Personally I really don't like KVPs in DBs because you cannot build application logic specific indixes anymore.
Just another approach would be not to model the specific columns at all. You create generic "custom attribute" tables like: Attribute1, Attribute2, Attribute3, Attribute4 (for the required data type etc...) You then add meta data to your database that describes what AttrX means for a specific type of printer label.
Again, it really depends on how you want to use that data in the end.
One risk is the table getting too wide. I used to maintain a horrible app that added 3 columns "automagically" when new values were added to some XML (for some reason it thought everything would be a string a date or a number- hence the creation of 3 columns).
There are other techniques like serializing a BLOB or designing the tables differently that may help.

Categories