Create a temporary table and then Select - c#

I have the following problem in an WPF application written in c#. I need to create some temporary tables on an sql server, do some joins from within the app an then select from the joined table. In the statistical language R I simply create two SQL Queries (one containing the #tables and one with the final select statement from a merge of several #tables) and execute them one after another. Doing the query in just one Query only returns NULL after the #temp tables have been created.
This is SQL statement #1:
DECLARE #from_time Date;
DECLARE #to_time Date;
CREATE TABLE #temp1
(
person_id float,
first_name varchar(100),
othercols...
)
INSERT INTO #temp1
SELECT DISTINCT
person_id, first_name, ...
FROM
campus.v_exam_registration_context context
FULL JOIN
campus.v_exam_timetable time
ON
CAST(context.examination_date AS DATETIME) = time.timetable_date
AND
context.examination_time_from = time.time_from
AND
context.examination_time_to = time.time_to
CREATE TABLE #temp2
(
exam_event_id float,
person_id float,
othercols...
)
INSERT INTO #temp2
SELECT DISTINCT
exam_event_id, person_id, excused, excused_reason, missed, modify_date, study_id, study_name,
person_exam_id, exam_in_course_id, subject, subject_unicode, personal_exam_no, exam_points, grade_description
FROM
campus.v_person_exam exam
WHERE
exam.person_id
IN
(
SELECT
#temp1.person_id
FROM
#temp1
WHERE
#temp1.attempt_counter > 1
AND
#temp1.timetable_date > #from_time
AND
#temp1.timetable_date < #to_time
)
CREATE TABLE #temp3
(
person_id float,
first_name varchar(100),
)
INSERT INTO #temp3
SELECT
#temp2.person_id, first_name, last_name, matriculation_number, attempt_counter, timetable_date, examination_date, semester, course_number, course_name,
exam_name, exam_type, component_name, course_area, module_number, module_name, credits,
FROM
#temp2
LEFT JOIN
#temp1
ON
#temp2.person_id = #temp1.person_id
AND
#temp2.exam_event_id = #temp1.exam_event_id
WHERE
#temp1.course_name IS NOT NULL
And this is #2:
SELECT DISTINCT T1.*
FROM
#temp3 T1
INNER JOIN
(
SELECT *
FROM
#temp3
WHERE
#temp3.attempt_counter > 1
AND
#temp3.exam_points > 4
AND
#temp3.timetable_date > #from_time
AND
#temp3.timetable_date < #from_time
) as T2
ON
T1.person_id = T2.person_id
AND
T1.exam_name = T2.exam_name
ORDER BY T1.last_name ASC, T1.course_name DESC, T1.timetable_date ASC
But now I'm required to translate the R Script into a standalone exe-file.
Here is my code block in c#:
private void queryButton_Click(object sender, RoutedEventArgs e)
{
List<Student> temp = new List<Student>();
string pass = #pass_box.Password;
string dsn = "CampusNet";
String ConnectionString =
"DSN=" + dsn + ";" +
"UID=" + uid + ";" +
"PWD=" + pass;
OdbcConnection conn = new OdbcConnection(ConnectionString);
using (conn)
{
if (conn.State.ToString() == "Open")
{
conn.Close();
}
try
{
conn.Open();
var command = new OdbcCommand(sqlcommand, conn); \\sqlcommand is read from a textfile
command.Parameters.AddWithValue("#from_time", from_time);
command.Parameters.AddWithValue("#to_time", to_time);
command.CommandTimeout = 120;
var resultCommand = command.ExecuteNonQuery();
var query = new OdbcCommand(sqlquery, conn); \\sqlquery is read from a textfile
query.Parameters.AddWithValue("#from_time", from_time);
query.Parameters.AddWithValue("#to_time", to_time);
var resultQuery = query.ExecuteReader();
Results_Box.AppendText(resultQuery.HasRows.ToString());
while (resultQuery.Read())
{
temp.Add(new Student{
name = !result.IsDBNull(3) ? result.GetString(3) : null
});
Results_Box.AppendText("Test"); \\only for testing purposes, works if I select from existing table
}
resultQuery.Close();
results = temp;
Results_Box.Document.Blocks.Add(new Paragraph(
new Run("There are " + temp.Count().ToString() + " Hits")));
System.Windows.Forms.MessageBox.Show("Connected");
conn.Close();
}
catch (Exception E)
{
Results_Box.Document.Blocks.Add(new Paragraph(new Run("Connection failed")));
Results_Box.Document.Blocks.Add(new Paragraph(new Run(E.ToString())));
}
}
}
I'm new to c#, so it seems that the first query is executed but deleted immediately afterwards and the second select statement returns 0 hits. If I select from non-temporary tables everything works fine. Since the first query has a duration approximately equal to the respective R-Query (25 sec), I suspect that the first one is correctly executed, but immediately deleted or the second query starts before the first is finished. Creating one combined query does not work neither in R nor in c#. I would like to stick to temporary tables rather than using ##tables, if possible.
Is there a special method to use for creating temp tables in c#? conn.open() is open during the whole runtime of using (conn){...} ?

Related

SQL IN Clause with string paramater list not listing all records

I'm passing a string variable to an IN Clause in sql (Stored Procedure). When declaring and setting the variable in sql I get back all the data that is required. But when setting the variable from c# I'm only receiving data based on the first status within that paramater.
I've got a function to split the statuses in the paramater list to retrieve the records:
ALTER FUNCTION [dbo].[fnSplit](
#sInputList VARCHAR(8000)
, #sDelimiter VARCHAR(10) = ';'
) RETURNS #List TABLE (item VARCHAR(8000))
BEGIN
DECLARE #sItem VARCHAR(8000)
WHILE CHARINDEX(#sDelimiter,#sInputList,0) <> 0
BEGIN
SELECT
#sItem=RTRIM(LTRIM(SUBSTRING(#sInputList,1,CHARINDEX(#sDelimiter,#sInputList,0)-1))),
#sInputList=RTRIM(LTRIM(SUBSTRING(#sInputList,CHARINDEX(#sDelimiter,#sInputList,0)+LEN(#sDelimiter),LEN(#sInputList))))
IF LEN(#sItem) > 0
INSERT INTO #List SELECT #sItem
END
IF LEN(#sInputList) > 0
INSERT INTO #List SELECT #sInputList
RETURN
END
My stored procedure is built like this:
ALTER procedure [dbo].[Get_RequestsAtEachStage]
(#managerRef int,
#status varchar(20))
as
BEGIN
WITH MaxStatusDate
as
(
select rs.requestID,rs.status from (
SELECT requestID,MAX([DateCreated]) AS MaxDate
FROM [LoanRequest].[dbo].[requestStatus]
GROUP BY RequestID) maxg
inner join [LoanRequest].[dbo].[requestStatus] rs on maxg.requestid = rs.requestid and maxg.MaxDate = rs.DateCreated
)
SELECT lr.ID, lr.serialNo, lr.model, lr.clientName, lr.address, lr.telephone, lr.contactName,
lr.swop, lr.substitueOfGoods, lr.printFunction, lr.copyFunction, lr.scanFunction,
lr.faxFunction, lr.controller, lr.controllerEmailAddress,
ml.Name, wl.Location, rt.requestType AS RequestTypeName, rs.status
FROM [dbo].[loanRequest] lr
INNER JOIN [dbo].[managersList] ml ON lr.managerRef = ml.ID
INNER JOIN [dbo].[warehouseList] wl ON lr.warehouseID = wl.ID
INNER JOIN [dbo].[requestType] rt ON lr.requestType = rt.ID
INNER JOIN MaxStatusDate rs ON lr.ID = rs.requestID
WHERE (#managerRef is null or lr.managerRef = #managerRef) AND rs.status IN (SELECT item FROM [dbo].[fnSplit](#status, ';'))
END
Based on the page the user access it will send through the appropriate statusses and retrieve the necessary records.
Setting the paramaters in sql as follows works perfect, I retrieve all the records:
DECLARE #managerRef INT
DECLARE #status NVARCHAR(100)
SET #managerRef = NULL
SET #status = 'Allocated;Readings Updated'
But, when I send it through c# within a string, it only retrieves records with the status of Allocated.:
string status = "Allocated;Readings Updated";
DataTable dtDevices = d.PopulateDevicesApproval(managerRef, status);
My method to retrieve the data from sql:
string filterstring = "";
filterstring = "Get_RequestsAtEachStage ";
cn = new SqlConnection(GetConnectionString());
SqlCommand myCmd = new SqlCommand(filterstring, cn);
myCmd.CommandType = CommandType.StoredProcedure;
cn.Open();
myCmd.Parameters.AddWithValue("#managerRef", managerRef);
myCmd.Parameters.AddWithValue("#status", status);
DataTable dt = new DataTable();
dt.Load(myCmd.ExecuteReader());
return dt;
Is there anything I am doing wrong?
--------- EDIT -----------
Running SELECT item FROM [dbo].fnSplit results from both c# and sql
Returning results from c#:
And returning results from sql:

Filtering detail table structure query language

Query Detail Table
Main Table
1 .PK
Detail Table
1. PK
2. Detail description.
If mysql i can group the main table PK for duplicate issue but how it implement in sql server ?Since sql server required something like aggregate
E.g
(vehicle) Main Table
Car
bikes
van
(vehicleItem) Detail Table
Car->item a,item b
bikes-> item a,item c..
van->item b ,item c
I want to filter in a query such as i want to filter item a only?Seem quite inefficient to call in sub loop.Even put all detail item it in a column consider as incorrect but when search item id(number) might be same number unless the item id are guid and distinct different.
** the main purpose is to output list not to sum crosstab :) ya.. grouping cool if you want to sum up.
var sql = "select * from vehicle ";
var command = new SqlCommand(connection,sql);
try {
var reader = command.ExecuteReader();
if (reader.HasRows) {
while (reader.Read())
if (!string.IsNullOrEmpty(Request.QueryString["ItemIdValue[]"])){
var d = Request.QueryString.GetValues("ItemIdValue[]");
if (d != null){
if (d[0].Contains("all")){
if (GetItemExist(" AND vehicleItem.itemId in (SELECT itemId from item ) ")){ }
}else{
if (GetItemExist(" AND vehicleItem.itemId IN ( " + itemFilter.Remove(itemFilter.Length - 1) + " ) ")){ }
}
}
}
}
}
}
At last thinking back, why not select main table and in subquery..
var sql = "select * from vehicle ";
if (!string.IsNullOrEmpty(Request.QueryString["itemIdValue[]"])){
var d = Request.QueryString.GetValues("itemIdValue[]");
if (d != null){
if (d[0].Contains("all")){
sql = sql + #"
AND vehicleId IN (
SELECT vehicleId
FROM vehicle
JOIN vehicleItem
ON vehicle.vehicleId = vehicleItem.vehicleId
WHERE vehicleItem.itemId in
(SELECT itemId from item)
) ";
} else{
for (var e = 0; e < d.Length; e++){
itemFilter += d[e] + ",";
_fieldVariable.Add("itemIdValue[]");
_valueVariable.Add(d[e]);
}
sql = sql + #"
AND vehicleId IN (
SELECT vehicleId
FROM vehicle
JOIN vehicleItem
ON vehicle.vehicleId = vehicleItem.vehicleId
WHERE vehicleItem.itemId in ( " + itemFilter.Remove(itemFilter.Length - 1) + " ) ) ";
}
}
}
When joining two tables t1 and t2, a result set containing all matching rows from table t1 and table t2 is created. When you only want one resulting row per t1's row, it is up to you to filter the join accordingly.
There are several possible ways to do that:
adapt the join's ON clause
group accordingly
specify a WHERE clause
As you wanted to use grouping in your request, you could use a query like this:
SELECT m.PK, min(d.Detail)
FROM Main m
LEFT JOIN Detail d on d.PK=m.pk
WHERE d.Detail = 'item a'
GROUP by m.PK
That way you will get one row per PK with the minimum value in detail. You can use other aggregate functions too. See https://msdn.microsoft.com/en-us/library/ms173454.aspx.
Edit: Added WHERE clause to filter by "item a".

How to i get result based on ids list passed as a varchar?

I am passing ids list as a varchar(500) and based upon that ids records are required.My sql code is
declare #Ids varchar(500) = '12964,12965,12966'
select *
from tblBooks
where BookID in (#Ids)
where BookID is varchar(50).Number of Ids can be 100.Converting #Ids into int gives following error
Conversion failed when converting the varchar value
'12964,12965,12966' to data type int
How do i find result as #Id are not converted into Int.
Use a table variable:
DECLARE #Ids TABLE (ID INT);
INSERT #Ids VALUES (12964),(12965),(12966);
SELECT *
FROM tblBooks
WHERE BookID in (SELECT ID FROM #Ids);
If you need to pass this to a procedure then you can use a table valued parameter:
CREATE TYPE dbo.ListOfInt AS TABLE (ID INT);
GO
CREATE PROCEDURE dbo.GetBooks #IDs dbo.ListOfInt READONLY
AS
BEGIN
SELECT *
FROM tblBooks
WHERE BookID in (SELECT ID FROM #Ids);
END
GO
DECLARE #IDs dbo.ListofInt;
INSERT #Ids VALUES (12964),(12965),(12966);
EXECUTE dbo.GetBooks #Ids;
Or From c#
var table = new DataTable();
table.Columns.Add("ID", typeof(int));
// ADD YOUR LIST TO THE TABLE
using (var connection = new SqlConnection("Connection String"))
using (var command = new SqlCommand("dbo.GetBooks", connection))
{
command.CommandType = CommandType.StoredProcedure;
var param = new SqlParameter("#Ids", SqlDbType.Structured);
param.TypeName = "dbo.ListofInt";
param.Value = table;
command.Parameters.Add(table);
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
// do something
}
}
}
Once the TYPE is in place, you don't even need to use a stored procedure. You can simply call a normal query:
using (var connection = new SqlConnection("Connection String"))
using (var command = new SqlCommand("SELECT * FROM tblBooks WHERE BookID IN (SELECT ID FROM #IDs)", connection))
{
var param = new SqlParameter("#Ids", SqlDbType.Structured);
param.TypeName = "dbo.ListofInt";
param.Value = table;
command.Parameters.Add(table);
connection.Open();
// ETC
}
Doing the split in c# using String.Split() and passing the list to SQL will be more efficient than any approach that does the split in SQL
You can write the query as this:
declare #Ids varchar(500) = '12964,12965,12966'
select *
from tblBooks
where ','+cast(BookID as varchar(500))+',' like '%,'+#Ids+',%';
But you don't want to do that because the performance is bad -- the query cannot use indexes.
Three other options. Use dynamic SQL and plug the list directly into the query. Or use a split function to split the string. Or use a table variable:
declare #ids table (id int);
insert into #ids(id)
select 12964 union all select 12965 union all select 12966;
select b.*
from tblBooks b
where b.BookId in (select id from #ids);
This won't work. SQL Server does not split strings for you implicitly and there is no built in string split function in SQL Server either.
If you are driving this via C# you can use Table value parameters. You can also pass your query through Dapper-Dot-Net which will automatically parameterize an "In" query.
If you really must do this in T-SQL, you can also use a string splitting logic here is a relatively concise one.
SELECT i.value('./text()[1]', 'int') [id] into #ids
FROM( values(CONVERT(xml,'<r>' + REPLACE(#Ids+left(##dbts,0),',','</r><r>') + '</r>')) ) a(_)
CROSS APPLY _.nodes('./r') x(i)
select *
from tblBooks a
join #ids i on i.id = a.bookId
Create this function:
CREATE FUNCTION [dbo].[SplitDelimiterString] (#StringWithDelimiter VARCHAR(8000), #Delimiter VARCHAR(8))
RETURNS #ItemTable TABLE (Item VARCHAR(8000))
AS
BEGIN
DECLARE #StartingPosition INT;
DECLARE #ItemInString VARCHAR(8000);
SELECT #StartingPosition = 1;
--Return if string is null or empty
IF LEN(#StringWithDelimiter) = 0 OR #StringWithDelimiter IS NULL RETURN;
WHILE #StartingPosition > 0
BEGIN
--Get starting index of delimiter .. If string
--doesn't contain any delimiter than it will returl 0
SET #StartingPosition = CHARINDEX(#Delimiter,#StringWithDelimiter);
--Get item from string
IF #StartingPosition > 0
SET #ItemInString = SUBSTRING(#StringWithDelimiter,0,#StartingPosition)
ELSE
SET #ItemInString = #StringWithDelimiter;
--If item isn't empty than add to return table
IF( LEN(#ItemInString) > 0)
INSERT INTO #ItemTable(Item) VALUES (#ItemInString);
--Remove inserted item from string
SET #StringWithDelimiter = SUBSTRING(#StringWithDelimiter,#StartingPosition +
LEN(#Delimiter),LEN(#StringWithDelimiter) - #StartingPosition)
--Break loop if string is empty
IF LEN(#StringWithDelimiter) = 0 BREAK;
END
RETURN
END
Then call it like this:
declare #Ids varchar(500) = '12964,12965,12966'
select *
from tblBooks
where BookID in (SELECT * FROM dbo.SplitDelimiterString(#ids,','))
one way is to cast int to varchar. many other ways....
select *
from tblBooks
where CAST(BookID as varchar(50)) in (#Ids)
related: Define variable to use with IN operator (T-SQL)

asp.net multiple table update statement

I need to turn this query into an update statement. I will have to update the values from fields. Everything is already in place but the update statement.
Here is the select version of the query:
SELECT i.GoLiveDate, i.FirstBonusRun, i.TechFName, i.TechLName, i.TechEmail, i.TechPhone, i.WebISPFName, i.WebISPLName,
i.WebISPEmail, i.WebISPPhone, i.FullFillFName, i.FullFillLName, i.FullFillEmail, i.FullFillPhone, d.FName,
d.LName, d.HomePhone, d.Email
FROM NC_Information i
INNER JOIN Distributor d
ON d.DistID = i.ClientID
WHERE clientID = #value
Is it possible to update two different tables from within the same query?
Here is the code I have so far:
public void Update (int ClientID)
{
using ( var conn = new SqlConnection( GeneralFunctions.GetConnectionString() ) )
using ( var cmd = conn.CreateCommand() )
{
conn.Open();
cmd.CommandText =
#"SELECT i.GoLiveDate, i.FirstBonusRun, i.TechFName, i.TechLName, i.TechEmail, i.TechPhone, i.WebISPFName, i.WebISPLName,
i.WebISPEmail, i.WebISPPhone, i.FullFillFName, i.FullFillLName, i.FullFillEmail, i.FullFillPhone, d.FName,
d.LName, d.HomePhone, d.Email
FROM NC_Information i
INNER JOIN Distributor d
ON d.DistID = i.ClientID
WHERE clientID = #value";
cmd.Parameters.AddWithValue( "#value", ClientID );
cmd.ExecuteNonQuery();
}
}
You can't update multiple tables in one statement, but you can use a transaction to make sure that the updates are contingent upon one another:
BEGIN TRANSACTION
UPDATE SomeTable
SET SomeColumn = 'Foo'
WHERE SomeID = 123
UPDATE AnotherTable
SET AnotherColumn = 'Bar'
WHERE AnotherID = 456
COMMIT
I think, you cannot directly do the update on two tables. But you can Optimize the query.
How?
OUTPUT keyword in Insert/Update/Delete Statement
The first Update Statement's Select Data(filtered data) can be reused using the below mentioned example.
CREATE TABLE #table1
(
id INT,
employee VARCHAR(32)
)
go
INSERT INTO #table1 VALUES
(1, 'name1')
,(2, 'name2')
,(3, 'name3')
,(4, 'name4');
GO
DECLARE #GuestTable TABLE
(
id INT,
employee VARCHAR(32)
);
update #table1
Set id = 33
OUTPUT inserted.* INTO #GuestTable
Where id = 3
The Data in the '#GuestTable' Table is filtered data and can be
reused.
select * from #GuestTable
drop table #table1
Alternatively, you can create a dataset with two datatables, and let the tableadaptermanager manage the updates.

SQL Server: Check if Child Rows Exist

I am working on a web application where there are many tables but two will suffice to illustrate my problem:
User
Order
Let us say that the User table has a primary key "UserID", which is a foreign key in the Order table called "CreatedBy_UserID".
Before deleting a User, I would like to check if the Order table has a record created by the soon-to-be deleted user.
I know that a SqlException occurs if I try to delete the user but let us say that I want to check beforehand that the Order table does not have any records created by this user? Is there any SQL code which I could run which will check all foreign keys of a table if that row is being referenced?
This for me is generally useful code as I could remove the option for deletion altogether if it can be detected that the user exists in these other tables.
I don't want a simple query (SELECT COUNT(*) FROM Order WHERE CreatedBy_UserID == #userID) because this will not work if I create another foreign key to the Order table. Instead I want something that will traverse all foreign keys.
Can this be done?
Below is code for an sp that I've used in the past to perform this task (please excuse the indenting):
create proc dbo.usp_ForeignKeyCheck(
#tableName varchar(100),
#columnName varchar(100),
#idValue int
) as begin
set nocount on
declare fksCursor cursor fast_forward for
select tc.table_name, ccu.column_name
from
information_schema.table_constraints tc join
information_schema.constraint_column_usage ccu on tc.constraint_name = ccu.constraint_name join
information_schema.referential_constraints rc on tc.constraint_name = rc.constraint_name join
information_schema.table_constraints tc2 on rc.unique_constraint_name = tc2.constraint_name join
information_schema.constraint_column_usage ccu2 on tc2.constraint_name = ccu2.constraint_name
where tc.constraint_type = 'Foreign Key' and tc2.table_name = #tableName and ccu2.column_name = #columnName
order by tc.table_name
declare
#fkTableName varchar(100),
#fkColumnName varchar(100),
#fkFound bit,
#params nvarchar(100),
#sql nvarchar(500)
open fksCursor
fetch next from fksCursor
into #fkTableName, #fkColumnName
set #fkFound = 0
set #params=N'#fkFound bit output'
while ##fetch_status = 0 and coalesce(#fkFound,0) <> 1 begin
select #sql = 'set #fkFound = (select top 1 1 from [' + #fkTableName + '] where [' + #fkColumnName + '] = ' + cast(#idValue as varchar(10)) + ')'
print #sql
exec sp_executesql #sql,#params,#fkFound output
fetch next from fksCursor
into #fkTableName, #fkColumnName
end
close fksCursor
deallocate fksCursor
select coalesce(#fkFound,0)
return 0
end
This will select a value of 1 if a row has any foreign key references.
The call you would need would be:
exec usp_ForeignKeyCheck('User','UserID',23)
There is no clean way to iterate through all FK columns where multiple exist. You'd have to build some dynamic SQL to query the system tables and test each in turn.
Personally, I wouldn't do this. I know what FKs I have: I'll test each in turn
...
IF EXISTS (SELECT * FROM Order WHERE CreatedBy_UserID == #userID)
RAISERROR ('User created Orders ', 16, 1)
IF EXISTS (SELECT * FROM Order WHERE PackedBy_UserID == #userID)
RAISERROR ('User packed Orders', 16, 1)
...
You wouldn't dynamically iterate through each property of some user object and generically test each one would you? You'd have code for each property
This code will give you a list of the foreign keys which are defined for a specifit table:
select distinct name from sys.objects
where object_id in ( select constraint_object_id from sys.foreign_key_columns as fk
where fk.Parent_object_id = (select object_id from sys.tables
where name = 'tablename') )
You can use transaction to check it.
I know it seems like stone ax, but it working fast and stable.
private bool TestUser(string connectionString, int userID)
{
var result = true;
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
var command = connection.CreateCommand();
var transaction = connection.BeginTransaction();
command.Connection = connection;
command.Transaction = transaction;
try
{
command.CommandText = "DELETE User WHERE UserID = " + userID.ToString();
command.ExecuteNonQuery();
transaction.Rollback();
}
catch
{
result = false;
}
}
return result;
}

Categories