Is there a way to query the database to retrieve information about the schema of a table? I'm interested in just getting a list of the Column names and whether they are primary keys; is this possible? I don't care about the type, just its name and whether it is a primary key.
Sample Table:
Table Organism
{
primary: int ID;
int Kingdom;
int Phylum;
int Class;
int Genus;
int Species;
nvarchar(50) Name;
}
Sample Usage:
List<Tuple<string, bool>> t = ReadTable("Organism");
t.ForEach(x => Console.WriteLine(x.Item2 ? x.Item2 + ": " + x.Item1 : x.Item1));
Sample Output:
True ID
Kingdom
Phylum
Class
Genus
Species
Name
I am using C#4.0 and SQL Server 2008 R2. I think this should be possible using the system tables but I don't know how to do it.
select col.*
from sys.columns col
join sys.tables tab on col.object_id = tab.object_id
where tab.name = #tabName
order by col.column_id
The system views contain a wealth of data and they are easy to use.
You can get field names with such hacky query
select * from Organism where 1=2
And Metadata can be retrieved with Information Schema Views , for example key usage:
SELECT * FROM databaseName.INFORMATION_SCHEMA.KEY_COLUMN_USAGE
Where TABLE_NAME='Organism'
Related
Here is a truncated example of what I'm trying to do:
var stuffTOSave = new List<SomeObject> {
public int OtherTableId { get; set; }
public List<Guid> ComponentIds { get; set; }
};
var sql = #"CREATE TABLE Components( ComponentId uniqueidentifier PRIMARY KEY )
INSERT INTO Components VALUES (#WhatGoesHere?)
SELECT * FROM OtherTable ot
JOIN Components c on c.ComponentId = ot.ComponentId
WHERE Id = #OtherTableId
DROP TABLE Components"
Connection.Execute(sql, stuffToSave);
I know from other SO questions that you can pass a list into an insert statement with Dapper, but I can't find any examples that pass a list as well as another parameter (in my example, OtherTableId), or that have a non-object list (List<Guid> as opposed to a List<SomeObject> that has properties with names to reference).
For the second issue, I could select the ComponentIds into a list to give them a name like:
stuffToSave.ComponentIds.Select(c => new { ComponentId = c })
but then I'm not sure what to put in my sql query so that dapper understands to get the ComponentId property from my list of ComponentIds (Line 7)
I would still like to know the real way of accomplishing this, but I have this workaround that uses string interpolation:
var sql = $#"CREATE TABLE Components( ComponentId uniqueidentifier PRIMARY KEY )
INSERT INTO Components VALUES ('{string.Join($"'),{Environment.NewLine}('", request.ComponentIds)}')
SELECT * FROM OtherTable ot
JOIN Components c on c.ComponentId = ot.ComponentId
WHERE Id = #OtherTableId
DROP TABLE Components"
I'm not worried about SQL Injection since this is just interpolating a list of Guids, but I'd rather avoid this method if possible.
These are my classes
Public Class Student
{
Public int Id{get;set;}
public string Name {get;set;}
}
Public Class Department
{
Public int Id{get;set;}
public string Name {get;set;}
public IList <Student> StudentList{get;set;}
}
These are my tables
Student Table
Id |Name |Department
-----------------
1 |aa | 1
2 |bb | 1
3 |cc | 2
Department Table
ID | Name
----------
1 | xxx
2 | yyy
I need the all data of department with the corresponding list of student by using stored procedure
CREATE PROCEDURE test
(
)
AS
BEGIN
select Id as Id,
Name as Name,
----here I need all the corresponding data from student table as StudentList
from Department
Is it possible.. If so Please help me.
Apparently I what want is I want to write another procedure and call it for StudentList,Is it possible
Use join, i choose Left Join here so in case if department does not have corresponding students it will still be retrieved.
SELECT d.ID
,d.Name
,s.ID
,s. Name
FROM Deparment d
LEFT JOIN Student s ON s.Department = d.ID
So from your example the query will return the following result :
ID, Name, Student ID Student Name
1 xxx 1 aa
1 xxx 2 bb
2 yyy 3 cc
Assuming that your stored procedure will accept department id as parameter and retrieve all students under this department, just add a condition in your query
WHERE d.ID = #DeptID
As #Eugen said, just process the result in your application.
What you describe can be done, but it is not the normal process. You can use for xml to return a column where all of the student information is encoded. Note that while this results in all of the data you want being returned in a single row, you will have to create and populate all of your objects by hand. None of the ORM's like Entity Framework or nHibernate will populate both your department and students. In fact, you will probably end up parsing a string by hand in order to create your students.
The most common practice today would be to use Entity Framework and let it do all of the work for you.
Here's some code to show how you can do as you request. Again, note that this is not recommended, and that it's just one way of doing this:
create table #s (id int, name varchar(max), departmentId int);
insert into #s (id, name, departmentId)
select * from (values (1 , 'aa', 1) ,(2 , 'bb', 1) ,(3 , 'cc', 2)
) g(id, name, departmentId);
create table #d (departmentId int, name varchar(max));
insert into #d (departmentId, name) select * from (values
(1 , 'xxx') ,(2 , 'yyy')
)g(departmentId, name)
select *, (select id, name, departmentId
from #s s where s.departmentID = d.departmentId for xml path
)students
from #d d
drop table #d
drop table #s;
Students for deparment 1 =
<row><id>1</id><name>aa</name><departmentId>1</departmentId></row>
<row><id>2</id><name>bb</name><departmentId>1</departmentId></row>
In particular, you may find this a bit heavy, 2016 should be able to do something similar with JSON, but if you'd like that in the meantime, you'll have to customize the output yourself. It can be done, and wouldn't be too difficult.
I have the below SQL query using the Query Builder in Visual Studio. As you can see the same user is duplicated 3 times, this is due to the user having 3 different skills. How can I merge the 3 skills together in the SQL query or in a ListView control so that it only displays one result instead of 3 and that the user has their 3 skills listed?
SELECT users.role_id, users.username, users.first_name, users.last_name, users.description, roles.role_id, roles.role, skills.skill_id, skills.user_id, skills.skill
FROM users
INNER JOIN roles ON users.role_id = roles.role_id
INNER JOIN skills ON users.user_id = skills.user_id
WHERE (users.role_id = 3)
Use For XML Path(''), Type. It is a bit of a hack, because you're really creating an XML string without a root and fashioning odd elements, but it works well. Be sure to include the Type bit, otherwise the XML trick will attempt to convert special characters, like < and & into their XML escape sequences (here is an example).
Here is a simplified version of your problem in a SQL Fiddle. Below is the relevant Select snippet.
SELECT users.user_id, users.first_name,
STUFF(
(SELECT ', ' + skill
FROM skills
WHERE users.user_id = skills.user_id
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
, 1, 2, '') AS skill_list
FROM users
Try using Stuff and For Xml
Here's the Fiddle:
http://sqlfiddle.com/#!6/fcf71/5
See if it helps, it's just a sample so you will have to change the column names.
You can use PIVOT on the Skill then group those skills into one column.
To make it simple, I test it with some sample data like the following:
CREATE SCHEMA _Test
CREATE TABLE _Test.SkillSet(SkillId INT IDENTITY(1,1) PRIMARY KEY, SkillName NVARCHAR(64))
INSERT INTO _Test.SkillSet(SkillName) VALUES('C/C++')
INSERT INTO _Test.SkillSet(SkillName) VALUES('C#')
INSERT INTO _Test.SkillSet(SkillName) VALUES('Java')
CREATE TABLE _Test.Employees(EmpId INT IDENTITY(1,1) PRIMARY KEY, FullName NVARCHAR(256))
INSERT INTO _Test.Employees(FullName) VALUES('Philip Hatt')
INSERT INTO _Test.Employees(FullName) VALUES('John Rosh')
CREATE TABLE _Test.Employee_Skill(EmpId INT FOREIGN KEY REFERENCES _Test.Employees(EmpId), SkillId INT FOREIGN KEY REFERENCES _Test.SkillSet(SkillId))
INSERT INTO _Test.Employee_Skill(EmpId, SkillId) VALUES(1, 1)
INSERT INTO _Test.Employee_Skill(EmpId, SkillId) VALUES(1, 2)
INSERT INTO _Test.Employee_Skill(EmpId, SkillId) VALUES(1, 3)
INSERT INTO _Test.Employee_Skill(EmpId, SkillId) VALUES(2, 2)
INSERT INTO _Test.Employee_Skill(EmpId, SkillId) VALUES(2, 3)
WITH tEmpSkill
AS
(SELECT A.EmpId, A.FullName, C.SkillName
FROM _Test.SkillSet C RIGHT JOIN
(
_Test.Employees A LEFT JOIN _Test.Employee_Skill B
ON A.EmpId = B.EmpId
)
ON B.SkillId = C.SkillId
)
SELECT * FROM tEmpSkill
PIVOT(COUNT(SkillName) FOR SkillName IN([C/C++], [C#], [Java])) AS Competency
The query above gives me an intermediate result
PIVOT RESULT
Now you can easily make a string containing all the skills needed for each employee. You can also search for some articles to use the PIVOT with unknown number of columns (skill sets), which may better serve your need.
Hope this can help.
These are my tables:
Equipment
-------------
ID INT (PKey)
Name CHAR(10)
Type CHAR(10)
LocationID INT (FKey)
Description TEXT
Location
-------------
LocationID INT (PKey)
Name CHAR(10)
Code INT
The user is given a list of Location.Code to select when inputing new Equipment. (This is how the user identifies the Equipment.LocationID.)
However if I input the data for Location.Code as Equipment.LocationID it will break the system. Is there better way of doing this? For example can I come up with a way to select the Location.LocationID and use the in place of Equipment.LocationID?
NOTE I can't change what the user selects, that has to remain the same.
For example (pseudo code):
string userInput = "110"; // Location.Code
SELECT LocationID FROM Location WHERE Code = #userInput;
userInput = LocationID; // LocationID = 1;
INSERT INTO Equipment(LocationID) VALUES (#userInput);
Is this the best way or is there a more efficient way?
merge two queries into one as below
INSERT INTO Equipment(LocationID) SELECT LocationID FROM Location WHERE Code = '110';
I have a number of user ID's. I am inserting these into a group table which will contain a column for the user's ID and the group ID. This will allow me to use the query "SELECT user_id FROM groups WHERE group_id = '3';" to retrieve user ID's of all the members of group 3.
My problem is that I currently have a list of all users IDs, which I got from a form, using the statements :
int i = 0;
String[] names = { Request.Form["usernames"]Split(' ') }; //retrieving names from form
List<int> user_ids = new List<int>();
foreach(string name in names){
int user_id = db.QueryValue("SELECT user_id FROM users WHERE username = name");
user_ids.Add(user_id); //now I have a list of all user_ids
}
I now wish to insert this data into the groups table, where all of the user_id values in the list will have the same group_ID. How can I do this?
//create a group from users
"INSERT INTO group (group_id, user_id) VALUES(?,?);
What you are talking about is a many-many relationship. You already have a users table:
**users**
userid
username
You need an additional table in the middle. The group table will just have something like:
**group***
groupid
groupName
You would then have a table in the middle. This table would look something like this:
**user_groups**
userid
groupid
You could still use your code to insert a user,
int i = 0;
String[] names = { Request.Form["usernames"]Split(' ') }; //retrieving names from form
List<int> user_ids = new List<int>();
foreach(string name in names){
int user_id = db.QueryValue("SELECT user_id FROM users WHERE username = name");
user_ids.Add(user_id); //now I have a list of all user_ids
}
After this, you would insert a group:
insert into group(groupName) values("Sample Group")
Then you could retrieve the group id and use that to insert into user_groups
select groupid from group where groupname="Sample Group"
insert into user_groups(userid,groupid) values(...
Also, the table structure should include primary keys and foreign keys (much like #sixlettervariables' answer)
In order to make this cleaner, you'll probably want to refactor your database setup slightly such that a third table relates users to groups:
users (user_id pk, ...)
groups (group_id pk, ...)
membership (user_id fk, group_id fk) unique(user_id, group_id)
When you needed to make a new group you simply insert into the groups table, obtain the group_id, and use that to populate the membership table for each of the users in that group.
You can use the select as the input to the insert
INSERT INTO group (group_id, user_id)
SELECT 1, user_id FROM users WHERE username in ("name1", name2")
You can join the names array back together with some commas.
You will need to iterate the user_ids list and do a separate insert statement for each user id.
You can insert all of the User ID's from one table into another using a sub-select and union statement as follows:
INSERT INTO group_table_name([user_id]) SELECT [user_id] FROM table_name
UNION ALL
By the way, you might want to change that table name since "group" is a keyword in SQL Server. Just a tip.
Insert Into GroupTable (GroupId, UserID) Select GroupID, USerID from UserTable group by GroupID, UserID)
This would work :)
Assuming you already know 3, you can do this without pulling the user ids into a local list and then inserting them individually (put in quotes specifically because the OP has their query in a similar string):
"INSERT dbo.group(group_id, user_id)
SELECT 3, user_id
FROM dbo.users
WHERE username = name;"
If you don't already know the group id, then please explain how you determined the group id should be 3.
EDIT based on further info.
CREATE TABLE dbo.Groups
(
GroupID INT IDENTITY(1,1) PRIMARY KEY,
GroupName NVARCHAR(255) NOT NULL UNIQUE
);
CREATE TABLE dbo.GroupUsers
(
GroupID INT NOT NULL FOREIGN KEY
REFERENCES dbo.Groups(GroupID),
UserID INT NOT NULL FOREIGN KEY
REFERENCES dbo.Users(UserID),
PRIMARY KEY(GroupID, UserID)
);
Now when you want to create a new group and add users to it:
DECLARE #GroupID INT;
INSERT dbo.Groups(GroupName) SELECT N'whatever (unique)';
SELECT #GroupID = SCOPE_IDENTITY();
Now you can loop through each user id:
INSERT dbo.GroupUsers(GroupID, UserID) SELECT #GroupID, <userid>;