I have a query which is returning two columns which represents the relationship between two entities (direct or indirect), for example the relationships show in the graph to the right would be represented by the data in the table to the right:
| From | To | 1 3 4
|------|----| o----o----o
| 1 | 3 | / \
| 1 | 4 | / \
| 1 | 5 | 2 o o 5
| 2 | 3 |
| 2 | 4 |
| 2 | 5 |
| 3 | 4 | 6 7
| 3 | 5 | o----o
| 6 | 7 |
What I want to do is group this data into a number of sets, equal to the number of distinct graphs described by the relationships (so 2 sets in the above example).
This grouping could happen as part of the database query (T-SQL) or once the data is in memory (C#).
It may not be pretty, but this will group the vertexes properly and only requires edges as a starting point. Note that the order of the vertexes for an edge does not matter.
-- Sample data.
declare #Edges as Table ( Vertex1 Int, Vertex2 Int );
insert into #Edges ( Vertex1, Vertex2 ) values
( 1, 3 ), ( 3, 4 ), ( 3, 2 ), ( 3, 5 ),
( 6, 7 );
select * from #Edges;
-- Create a working table that assigns each vertex to a unique "set".
declare #Sets as Table ( SetId Int, Vertex Int );
insert into #Sets ( SetId, Vertex )
select Row_Number() over ( order by Vertex ), Vertex from (
select distinct Vertex1 as Vertex from #Edges
union
select distinct Vertex2 from #Edges ) as PH;
select * from #Sets;
-- Update the working table to group vertexes into sets:
-- For each vertex update the SetId to the minimum SetId of all of the vertexes one edge away.
-- Repeat until nothing changes.
declare #UpdatedRows as Int = 42;
while #UpdatedRows > 0
begin
update NS
set SetId = MinSetId
from (
select S.SetId, S.Vertex,
( select Min( SetId ) from #Sets where Vertex in (
select S.Vertex union
select Vertex1 from #Edges where Vertex2 = S.Vertex union
select Vertex2 from #Edges where Vertex1 = S.Vertex )
) as MinSetId
from #Sets as S ) as NS
where SetId != MinSetId;
set #UpdatedRows = ##RowCount;
select * from #Sets; -- Show the work as it progresses.
end
-- The SetId values can be collapsed using Dense_Rank .
select Dense_Rank() over ( order by SetId ) as SetId, Vertex
from #Sets;
Related
Is it possible to emulate the following MySQL query:
SELECT * FROM `tbl` ORDER BY `date` DESC LIMIT X, 10
(X is a parameter)
in MS Access?
While the Access/JET TOP keyword does not directly provide an OFFSET capability, we can use a clever combination of TOP, a subquery, and a "derived table" to obtain the same result.
Here is an example for getting the 10 rows starting from offset 20 in a Person table in ORDER BY Name and Id...
SELECT Person.*
FROM Person
WHERE Person.Id In
(
SELECT TOP 10 A.Id
FROM [
SELECT TOP 30 Person.Name, Person.Id
FROM Person
ORDER BY Person.Name, Person.Id
]. AS A
ORDER BY A.Name DESC, A.Id DESC
)
ORDER BY Person.Name, Person.Id;
Essentially, we query the top 30, reverse the order, query the top 10, and then select the rows from the table that match, sorting in forward order again. This should be fairly efficient, assuming the Id is the PRIMARY KEY, and there is an index on Name. It might be that a specific covering index on Name, Id (rather than one on just Name) would be needed for best performance, but I think that indexes implicitly cover the PRIMARY KEY.
Another way - Let say you want from 1000 to 1999 records in a table called table1 (of course if you have that many records) you can do something like this.
MSSQL
SELECT *
FROM table1 LIMIT 1000, 1999;
MS Access
SELECT TOP 1000 *
FROM table1
Where ID NOT IN (SELECT TOP 999 table1.ID FROM table1);
To break this down
SELECT TOP NumA *
FROM table1
Where ID NOT IN (SELECT TOP NumB table1.ID FROM table1);
UpperLimit = 1999
LowerLimit = 1000
NumA = UpperLimit - LowerLimit + 1
ex. 1000 = 1999 - 1000 + 1
NumB = LowerLimit -1
ex. 999 = 1000 - 1
A better query would be:
SELECT Users.*
FROM Users
WHERE Users.id In
(
SELECT TOP X A.id
FROM [
SELECT TOP Y Users.*
FROM Users
ORDER BY Users.reg_date DESC
]. AS A
ORDER BY A.reg_date ASC
)
ORDER BY Users.reg_date DESC
Where
if((totalrows - offset) < limit) then
X = (totalrows - offset)
else
X = limit
And:
Y = limit + offset
For example, if total_rows = 12, and we set the limit to 10 (show 10 users per page), and the offset is calculated as p * limit - (limit) where p is the number of the current page, hence in the first page (p = 1) we will get: X = 12 and Y = 10, on the second X = 2 and Y = 20. The list of users is ordered by registration date (descending).
Simple and fastest solution.
myTable {ID*, Field2, Filed3...}
Assume your SortOrder contain primary KEY only
SELECT TOP PageItemsCount tb01.*
FROM myTable AS tb01
LEFT JOIN (
SELECT TOP OffsetValue ID FROM myTable ORDER BY ID ASC
) AS tb02
ON tb01.ID = tb02.ID
WHERE ISNULL(tb02.ID)
ORDER BY tb01.ID ASC
SortOrder based on other fields with duplicated values, in this case you must include your primary key in SortOrder as last one.
For exemple, myTable
+-------+--------+--------+
| ID | Field2 | Filed3 |
+-------+--------+--------+
| 1 | a1 | b |
| 2 | a | b2 |
| 3 | a1 | b2 |
| 4 | a1 | b |
+-------+--------+--------+
SELECT TOP 2 * From myTable ORDER BY FIELD2;
+-------+--------+--------+
| ID | Field2 | Filed3 |
+-------+--------+--------+
| 2 | a | b2 |
| 4 | a1 | b |
| 3 | a1 | b2 |
| 1 | a1 | b |
+-------+--------+--------+
SELECT TOP 2 * From myTable ORDER BY FIELD2, FIELD3;
+-------+--------+--------+
| ID | Field2 | Filed3 |
+-------+--------+--------+
| 2 | a | b2 |
| 4 | a1 | b |
| 1 | a1 | b |
+-------+--------+--------+
But if we add ID to sort order [AS LAST IN FIELDS LIST]
SELECT TOP 2 * From myTable ORDER BY FIELD2, ID;
+-------+--------+--------+
| ID | Field2 | Filed3 |
+-------+--------+--------+
| 2 | a | b2 |
| 1 | a1 | b |
+-------+--------+--------+
Final request
SELECT TOP PageItemsCount tb01.*
FROM myTable AS tb01
LEFT JOIN (
SELECT TOP OffsetValue ID FROM myTable ORDER BY Field2 ASC, ID
) AS tb02
ON tb01.ID = tb02.ID
WHERE ISNULL(tb02.ID)
ORDER BY tb01.Field2 ASC, tb01.ID
You can definitely get the the equivalent of "Limit" using the top keyword. See:
Access Database LIMIT keyword
No, JET SQL does not have a direct equivalent. As a workaround, you could add a WHERE clause that selects an ordered/id column between two values.
If possible, you can also use pass-through queries to an existing MySQL/other database.
While TOP in MS-Access can limit records returned, it does not take two parameters as with the MySQL LIMIT keyword (See this question).
|No |Type |Num |
| 1 | a | 1 |
| 1 | a | 2 |
| 2 | b | 1 |
| 2 | b | 1 |
| 3 | b | 2 |
| 3 | b | 2 |
I have a Table with columns(No and Type) and I want to create columns(Num) with values. How can I do that in C#?
If you want to split each type into two groups based on no, then you can use window functions:
select t.*,
(case when row_number() over (partition by type order by no) <= 0.5 * count(*) over (partition by type)
then 1 else 2
end) as num
from t;
I'd suggest using dense_rank:
declare #tbl table ([No] int, [Type] varchar(1),[Num] int);
insert into #tbl values
( 1 , 'a' , 1 ),
( 1 , 'a' , 2 ),
( 2 , 'b' , 1 ),
( 2 , 'b' , 1 ),
( 3 , 'b' , 2 ),
( 3 , 'b' , 2 );
select [No], [Type],
dense_rank() over (partition by [Type] order by [Num])
from #tbl
create a method like below and call it
public ExecuteCommand(string columnName,List<string> values)
{
//here 'command' is instance variable of IDbCommand class, to execute command text in database
string sql = "alter table TableName add ColumnName DatatypeOfColumn ";
command.CommandText = sql;
command.ExecuteScalar();
foreach(string value in values)
{
sql = "update table TableName set ColumnName="+ value + " where condition ";
command.CommandText = sql;
command.ExecuteScalar();
}
}
I need to analyze data from SQL server table. Table contains data connected with qualifications of all employyes in the company and has the following structure (simplified):
| User | Qualification | DateOfQualificationAssignment |
| user000 | Junior | 2014-01-15 |
| user000 | Middle | 2014-02-15 |
| user001 | Middle | 2014-02-02 |
| user001 | Senior | 2014-03-18 |
| user002 | Senior | 2014-02-19 |
| user003 | Junior | 2014-03-04 |
I need the way to determine number of employees having given qualification for the concrete date. It should be some sort of analyze("Qualification", "Date") function returning the folowing for these types of input data:
analyze("Junior", '2014-01-20') - returns 1 (it is user user000)
analyze("Junior", '2014-02-20') - returns 0 (because user000 became Middle on 2014-02-15)
analyze("Middle", '2014-02-25') - returns 2 (because user000 and user001 are having Middle qualification on 2014-02-25)
analyze("Middle", '2014-03-28') - returns 1 (user000 is still Middle, but user001 became Senior on 2014-03-18)
Currently I have no idea how to handle this efficiently. What approach can be used to achieve my goal?
Think this should satisfy your requirements:
create function dbo.analyze(#qualification varchar(50), #date date)
returns int
as
begin
declare #result int;
with cte
as
(
select t.*, rank() over (partition by t.[User] order by t.DateOfQualificationAssignment desc) r
from theTable t -- no clue what the real table is named
where t.DateOfQualificationAssignment < #date
)
select #result = count(*)
from cte
where cte.r = 1 and cte.Qualification = #qualification
return #result;
end
go
Tested with your data:
create table theTable
(
[User] varchar(50) not null,
Qualification varchar(50) not null,
DateOfQualificationAssignment date not null
)
go
insert into theTable([User],Qualification,DateOfQualificationAssignment)
values
('user000','Junior','20140115'),
('user000','Middle','20140215'),
('user001','Middle','20140202'),
('user001','Senior','20140318'),
('user002','Senior','20140219'),
('user003','Junior','20140304')
go
and the results:
select dbo.analyze('Junior','20140120') --returns 1
go
select dbo.analyze('Junior','20140220') --returns 0
go
select dbo.analyze('Middle','20140225') --returns 2
go
select dbo.analyze('Middle','20140328') --returns 1
go
Use row_number() over() in a derived table to enumerate the rows on DateOfQualificationAssignment descending partitioned by User where DateOfQualificationAssignment is less than the date you want to check on.
In the main query you count the rows with the enumerated value 1 and Qualification.
SQL Fiddle
MS SQL Server 2012 Schema Setup:
create table T
(
[User] char(7),
Qualification char(6),
DateOfQualificationAssignment date
)
insert into T values
('user000', 'Junior', '2014-01-15'),
('user000', 'Middle', '2014-02-15'),
('user001', 'Middle', '2014-02-02'),
('user001', 'Senior', '2014-03-18'),
('user002', 'Senior', '2014-02-19'),
('user003', 'Junior', '2014-03-04')
Query 1:
declare #Qualification char(6) = 'Middle'
declare #Date date = '2014-03-28'
select count(*)
from (
select T.Qualification,
row_number() over(partition by T.[User] order by T.DateOfQualificationAssignment desc) as rn
from T
where T.DateOfQualificationAssignment < #Date
) as T
where T.rn = 1 and
T.Qualification = #Qualification
Results:
| COLUMN_0 |
|----------|
| 1 |
I want to show data like pivot grid. I am right now showing data like following. Please see following image or click on link.
http://screencast.com/t/CWeGy0vi
But I want above data like following:
http://screencast.com/t/ZTb2wk4cdmB
Any suggestion how to achieve this. Starting point. Should I use repeater?
Without seeing your table structure, etc. it is hard to give an exact answer. But I can suggest how you could perform this in SQL. You have some previous questions tagged with sql server so I am guessing that.
You can do this using both an UNPIVOT and a PIVOT:
;with unpiv as
(
select activity,
work,
Location+'_'+col as col,
value
from
(
select activity,
work,
cast(AssignedTasks as varchar(50)) AssignedTasks,
cast(CompletedTasks as varchar(50)) AchievedTasks,
Location
from yourtable
) src
unpivot
(
value
for col in (AssignedTasks, AchievedTasks)
) unpiv
),
piv as
(
select Activity,
work,
London_AssignedTasks,
London_AchievedTasks,
Geneva_AssignedTasks,
Geneva_AchievedTasks,
row_number() over(partition by activity order by activity, work) rn
from unpiv
pivot
(
max(value)
for col in (London_AssignedTasks, London_AchievedTasks,
Geneva_AssignedTasks, Geneva_AchievedTasks)
) piv
)
select case when rn = 1 then activity else '' end activity,
work,
London_AssignedTasks,
London_AchievedTasks,
Geneva_AssignedTasks,
Geneva_AchievedTasks
from piv
See SQL Fiddle with Demo.
The result is:
| ACTIVITY | WORK | LONDON_ASSIGNEDTASKS | LONDON_ACHIEVEDTASKS | GENEVA_ASSIGNEDTASKS | GENEVA_ACHIEVEDTASKS |
-------------------------------------------------------------------------------------------------------------------
| Activity 1 | Task 1 | 10 | 8 | 1 | 1 |
| | Task 2 | 15 | 15 | 100 | 25 |
| Activity 2 | Task 1 | 5 | 5 | 0 | 0 |
| | Task 2 | 0 | 0 | 2 | 2 |
| Activity 3 | Task 1 | 10 | 10 | 50 | 40 |
Edit #1, If you have an unknown or dynamic number of Locations then you can use dynamic SQL to return the result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(Location+'_'+t.tasks)
from yourtable
cross apply
(
select 'AssignedTasks' tasks
union all
select 'AchievedTasks'
) t
group by location, tasks
order by location
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = ';with unpiv as
(
select activity,
work,
Location+''_''+col as col,
value
from
(
select activity,
work,
cast(AssignedTasks as varchar(50)) AssignedTasks,
cast(CompletedTasks as varchar(50)) AchievedTasks,
Location
from yourtable
) src
unpivot
(
value
for col in (AssignedTasks, AchievedTasks)
) unpiv
),
piv as
(
select Activity,
work,
row_number() over(partition by activity order by activity, work) rn,
'+#cols+'
from unpiv
pivot
(
max(value)
for col in ('+#cols+')
) piv
)
select case when rn = 1 then activity else '''' end activity,
work,
'+#cols+'
from piv'
execute(#query)
See SQL Fiddle with Demo
I'm not an expert at SQL and i'm not even sure if this type of query is doable.
I want to return a count(*) for each "MediaTypeID" for each Month based off of "MediaDate".
Any help would be greatly appreciated!
Thanks.
My Table looks like:
The Table Data looks like:
1 | 1 | Funny Cat Video | 2006-01-25 00:00:00.000
2 | 1 | Funny Dog Video | 2006-01-20 00:00:00.000
3 | 2 | Angry Birds Game | 2006-03-13 00:00:00.000
4 | 4 | Blonde Joke | 2006-03-16 00:00:00.000
5 | 3 | Goofy Clown Picture | 2006-02-27 00:00:00.000
6 | 2 | Racing Game | 2006-02-10 00:00:00.000
7 | 1 | Star Wars Video | 2006-07-15 00:00:00.000
The query would return 12 rows of results for Jan-Dec looking like:
Month | MediaTypeID1Count | MediaTypeID2Count | MediaTypeID3Count | MediaTypeID4Count
Jan | 400 | 255 | 15 | 65
Feb | 100 | 25 | 75 | 35
Mar | 320 | 155 | 50 | 99
Apr | 56 | 0 | 98 | 313
This type of data transformation is known as a PIVOT. SQL Server 2005+ has a pivot function that can be implemented:
select month,
[1] MediaType1_count,
[2] MediaType2_count,
[3] MediaType3_count,
[4] MediaType4_count
from
(
select
mediatypeid,
datename(m, mediadate) Month,
datepart(m, mediadate) monnum
from yourtable
) src
pivot
(
count(mediatypeid)
for mediatypeid in ([1], [2], [3], [4])
) piv
order by monnum
See SQL Fiddle with Demo
If you have an unknown number of values that you want to transpose into columns, then you can use dynamic sql:
DECLARE #cols AS NVARCHAR(MAX),
#colNames AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(mediatypeid)
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #colNames = STUFF((SELECT distinct ', ' + QUOTENAME(mediatypeid) +' as MediaType' + cast(mediatypeid as varchar(50))+'_Count'
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT month,' + #colNames + ' from
(
select mediatypeid,
datename(m, mediadate) Month,
datepart(m, mediadate) monnum
from yourtable
) x
pivot
(
count(mediatypeid)
for mediatypeid in (' + #cols + ')
) p
order by monnum'
execute(#query)
See SQL Fiddle with Demo
The result will look like:
| MONTH | MEDIATYPE1_COUNT | MEDIATYPE2_COUNT | MEDIATYPE3_COUNT | MEDIATYPE4_COUNT |
----------------------------------------------------------------------------------------
| January | 2 | 0 | 0 | 0 |
| February | 0 | 1 | 1 | 0 |
| March | 0 | 1 | 0 | 1 |
| July | 1 | 0 | 0 | 0 |
I think this may be what you are looking for. The following will simply return the count based off the media type id, then group by year and then its month
Select MediaTypeID, datepart(year, MediaDate), datepart(month, MediaDate), count(*)
From Media
Group by MediaTypeID, datepart(year, MediaDate), datepart(month, MediaDate)
In my opinion, this could be a good challenge for you to start with this code:
Declare #T Table (
MediaID BigInt Primary Key Identity(1, 1)
, MediaTypeID BigInt
, MediaTitle Nvarchar(50)
, MediaDate DateTime
);
Insert #T (
MediaTypeID
, MediaTitle
, MediaDate
) Select 1
, 'Funny Cat Video'
, '2006-01-25 00:00:00.000'
Union
Select 1
, 'Funny Dog Video'
, '2006-01-20 00:00:00.000'
Union
Select 2
, 'Angry Birds Game'
, '2006-03-13 00:00:00.000'
Union
Select 4
, 'Blonde Joke'
, '2006-03-16 00:00:00.000'
Union
Select 3
, 'Goofy Clown Picture'
, '2006-02-27 00:00:00.000'
Union
Select 2
, 'Racing Game'
, '2006-02-10 00:00:00.000'
Union
Select 1
, 'Star Wars Video'
, '2006-07-15 00:00:00.000'
;
Select Month.Title
, Count(Type01_Result.MediaID) As MediaTypeID_1
, Count(Type02_Result.MediaID) As MediaTypeID_2
, Count(Type03_Result.MediaID) As MediaTypeID_3
, Count(Type04_Result.MediaID) As MediaTypeID_4
From (
Select 1 As Id
, 'Jan' As Title
Union
Select 2
, 'Feb'
Union
Select 3
, 'March'
Union
Select 4
, 'April'
Union
Select 5
, 'May'
Union
Select 6
, 'June'
Union
Select 7
, 'July'
Union
Select 8
, 'Aug'
Union
Select 9
, 'Sept'
Union
Select 10
, 'Nov'
Union
Select 11
, 'Oct'
Union
Select 12
, 'Dec'
) As Month
Left Outer Join
#T As Type01_Result
On Type01_Result.MediaTypeID = 1
And DatePart(Month, Type01_Result.MediaDate) = Month.Id
Left Outer Join
#T As Type02_Result
On Type02_Result.MediaTypeID = 2
And DatePart(Month, Type02_Result.MediaDate) = Month.Id
Left Outer Join
#T As Type03_Result
On Type03_Result.MediaTypeID = 3
And DatePart(Month, Type03_Result.MediaDate) = Month.Id
Left Outer Join
#T As Type04_Result
On Type04_Result.MediaTypeID = 4
And DatePart(Month, Type04_Result.MediaDate) = Month.Id
Group By Month.Title
;
The only thing is that you should be carefull about the year value and how many years you wanna include in the output, because this result will give you the summation of all records in each month no matter which year the date of the record is. It may cause some confusion in the result.
(Remember that I always set the tab size to 8 characters in the Options of Management Studio, so I suggest you do that to see the correct style of writing T-SQL)
Hope it helps you.
Cheers