mysql_db_query for mssql in php - sql

What is mssql function that have similar with mysql_db_query()?
and what about mysql_insert_id()?

Look at the PHP Mssql documentation. The functions you're looking for are mssql_query() and the following:
<?php
function mssql_insert_id() {
$id = 0;
$res = mssql_query("SELECT ##identity AS id");
if ($row = mysql_fetch_array($res, MYSQL_ASSOC)) {
$id = $row["id"];
}
return $id;
}
?>

I think the closest you will come to this in T-SQL is the following:
Use <MyDatabase>
Exec ('Select <ColumnList> From <SomeTable>')

In MSSQL, you can use the SCOPE_IDENTITY() function to get the last ID created by the given connection, in the same scope. Put them both together so you get the ID back just after row creation.
INSERT INTO myTable (field1,field2) VALUES(val1,val2); SELECT SCOPE_IDENTITY() AS myId;
Note, you could also use ##IDENTITY, but that returns the last ID created, regardless of scope, so if you inserted a new row, and a trigger/stored procedure fired and inserted something into another table, ##IDENTITY would return that ID.

Related

IF NOT EXISTS working in query mode but not working properly in stored procedure call from code

I have a stored procedure that does something like this:
MY_STORED_PROCEDURE:
IF NOT EXISTS(SELECT 1 FROM MY_TABLE WHERE MY_COLUMN1 = #MY_COLUMN1_VALUE_FROM_CODE AND MY_COLUMN2 = #MY_COLUMN2_VALUE_FROM_CODE)
BEGIN
--INSERT INTO MY_TABLE
This stored procedure is called automatically from C# code.
Now, this means that:
if we try to insert an entry with NEW VALUES for combination COLUMN1 and COLUMN2, then it should be inserted into MY_TABLE.
AND
If we try to insert an entry with EXISTING VALUES for combination COLUMN1 and COLUMN2, then it should not be inserted into MY_TABLE.
However, what happens is that the insertions are happening ALL THE TIME, independent of "IF NOT EXISTS" statement.
I already checked the "IF NOT EXISTS" statement directly from the query builder of SSMS and it seems that nothing is wrong with that statement.
Is there something that I do not take into consideration? What could be the error here?
This is also my C# code for invoking my SP:
public void InsertIntoMyTable(List<SqlParameter> parameters, SqlConnection connection)
{
DataTable dt = new DataTable();
SqlCommand command = new SqlCommand("MY_STORED_PROCEDURE", connection)
{
CommandType = CommandType.StoredProcedure
};
if (parameters != null)
{
foreach (SqlParameter parameter in parameters)
{
command.Parameters.Add(parameter);
}
}
dt.Load(command.ExecuteReader());
}
NOTE: MY_STORED_PROCEDURE is being called from different sources. So it is also possible that this procedure is being called at the SAME TIME from 1+ sources. I don't know if this can have some effects on my problem or not. I am reporting it, just in case it relates.
Thanks!
First, `NOT EXISTS* is the wrong way to do this, because it introduces race conditions. What you appear to want is a unique constraint. So add this as a constraint or index and then check for errors:
alter table t add constraint unq_my_table_column1_column2 on my_table (column1, column2);
Then simply do the insert . . . but in the TRY/CATCH block:
begin try
insert into my_table ( . . . )
values ( . . .);
end try;
begin catch
. . .
end catch;
If I had to speculate on the problem, then you have an issue with the parameters. Perhaps they are declared incorrectly -- such as varchar() with no length. Or perhaps they are being passed in incorrectly, as NULLs.
Something similar happened to me sometime ago because I didn't add SET NOCOUNT ON; at the beginning of my stored procedure.

Use trigger after multiple insert to update log table

I have the following trigger:
ALTER TRIGGER [Staging].[tr_UriData_ForInsert]
ON [Staging].[UriData]
FOR INSERT
AS
BEGIN
DECLARE #_Serial NVARCHAR(50)
DECLARE #_Count AS INT
IF ##ROWCOUNT = 0
RETURN
SET NOCOUNT ON;
IF EXISTS(SELECT * FROM inserted)
BEGIN
SELECT #_Count = COUNT(Id) FROM inserted
SELECT #_Serial = SerialNumber FROM inserted
INSERT INTO [Staging].[DataLog]
VALUES (CURRENT_TIMESTAMP, #_Serial + ': Data Insert --> Rows inserted: ' + #_Count, 'New data has been received')
END
END
The table receives multiple rows at once. I want to be able to add one row in the log table to tell me the insert has happened.
It works great with one row being inserted, but with multiple rows, the trigger doesn't fire. I have read other items on here and it is quite clear that you shouldn't use ROW_NUMBER().
In summary: I want to update my log table when a multiple row insert happens in another table called UriData.
The data is inserted from C# using the following:
using (var sqlBulk = new SqlBulkCopy(conn, SqlBulkCopyOptions.Default, transaction))
{
sqlBulk.DestinationTableName = tableName;
try
{
sqlBulk.WriteToServer(dt);
}
catch(SqlException sqlEx)
{
transaction.Rollback();
var msg = sqlEx.Message;
return false;
}
finally {
transaction.Commit();
conn.Close();
}
}
I don't want to know what is being inserted, but when it has happened, so I can run a set of SPROCS to clean and pivot the data.
TIA
The problem is your trigger assumes that only one row will be updated. A scalar variable can only have 1 value. So, for example, the statement SELECT #_Serial = SerialNumber FROM inserted will set #_Serial with the last value returned from the object inserted.
Treat your data as what it is, a dataset. This is untested, however, I suspect this gives you the result you want:
ALTER TRIGGER [Staging].[tr_UriData_ForInsert]
ON [Staging].[UriData]
FOR INSERT
AS
BEGIN
--No need for a ROWCOUNT. If there are no rows, then nothing was inserted, and this trigger won't happen.
INSERT INTO [Staging].[DataLog] ({COLUMNS LIST})
SELECT CURRENT_TIMESTAMP,
SerialNumber + ': Data Insert --> Rows inserted: ' +
CONVERT(varchar(10),COUNT(SerialNumber) OVER (PARTITION BY SerialNumber)), --COUNT returns an INT, so this statement would have failed with a conversion error too
'New data has been received'
FROM inserted;
END
Please note my comments or sections in braces ({}).
Edit: Sean, who has since deleted his answer, used GROUP BY. I copied what exact method you had, however, GROUP BY might well be the clause you want, rather than OVER.
So after a lot of digging and arguing, my hosting company told me that they have disabled bulk inserts of any kind, without bothering to notify their customers.

SQL query to update column in a table

I have a code based website in which an employee has to update their reward points by the coupon code provides them and when that code reflect their account means when points are updated in their account they are able to shop in the website. But there is a restriction for the code that code is deleted once used. Sometimes I found a query from customers that they update their account with the code provided but code did not reflect the account and deleted from the database and so thereafter they are not able to use the code again now I want that code only deleted when the code update points in their account. I have an another table named customer_reward where code saved after add points in the customers account but the code that not reflect account recharge is not saved in that table so I want that code only delete when that code is saved in the customer_reward table.
the complete code is given below:
<?php
if(isset($_POST['sub'])){
$db_host="localhost";
$db_username="root";
$db_password="";
$db_name="14";
$con=mysql_connect("$db_host", "$db_username", "$db_password") or die("could not connect to mysql!!!");
if($con=="")
{
echo "Database not connected!!!!";
}
else
{
$isdb=mysql_select_db("$db_name") or die("database not available!!!!");
if($isdb=="")
{
echo "database not selected!!!!";
}
else
{
$emp_ID=$_POST['emp_ID'];
$code=$_POST['code'];
$query = mysql_query("select * from oc_abhireward where `Code`='$code'") or die (mysql_error());
$data=mysql_fetch_assoc($query);
$code_db=$data['Code'];
$points_db=$data['Point'];
if($code==$code_db)
{
$query1 = mysql_query("select * from oc_customer where `emp_ID`='$emp_ID'") or die (mysql_error());
$data1=mysql_fetch_assoc($query1);
$customer_id=$data1['customer_id'];
$query2=mysql_query("INSERT INTO `oc_customer_reward` (customer_id, order_id, description, Code, points, date_added) VALUES ($customer_id, 0, 'rewarded', '$code', $points_db, NOW());");
$query4=mysql_query("INSERT INTO `oc_customer_recharge`(emp_ID, Code, points, date_added) VALUES ('$emp_ID', '$code', $points_db, NOW());");
if ($code==$code_db)
{
query5 = mysql_query("select * from oc_customer_recharge where Code='$code'")or die (mysql_error());
$data2=mysql_fetch_assoc($query4);
$emp_ID=$data2['emp_ID'];
$query6 = mysql_query("DELETE FROM oc_abhireward WHERE Code='$code'");
}
else
{
exit();
}
header("location:http://localhost/14/index.php?route=account/account");
exit();
}
else
{
}
}
}
}
?>
What's the relationship between Employee and Customer?
You are querying oc_customer by emp_ID, and getting customer_id from it. So is oc_customer unique on emp_id? If not, then the customer you end up getting (and so the one you'll apply the reward to) is effectively random. Instead, you need to pass in the customer's customer_id rather than the emp_id.
One other thing; You're using the "$POST"ed values directly in the SQL statements. That opens you up to a SQL-Injection attack. Check out How can I prevent SQL injection in PHP?
The best way possible here could be creating a trigger (for deleting the code) that will fired only when the update of points has been made.
CREATE TRIGGER trigger_name
AFTER INSERT
ON table_name FOR EACH ROW
BEGIN
-- logic for deleting the corresponding CODE
END;
Hope this will bring you closer to what you seek.
Ak
I think the problem is that you are really not veryfing whether a record was inserted in your oc_customer_reward table.
There are multiple ways of solving this problem.
You can modify your delete query to check oc_customer_reward table. This could be something on the lines of:
DELETE Table1
FROM Table1
INNER JOIN Table2 ON Table1.ID = Table2.ID
Create a trigger which will delete data in oc_reward table whenever a record is inserted in oc_customer_reward. You can look up triggers here
CREATE TABLE reward_code_table
(reward_code INT, random_col VARCHAR(50))
;
INSERT INTO reward_code_table
(`reward_code`, `random_col`)
VALUES
(1, 'First code'),
(2, 'Second code'),
(3, 'Third code')
;
CREATE TABLE insert_code_table
(customer_code INT, another_random_col VARCHAR(50))
;
DELIMITER //
CREATE TRIGGER del_after_insert
AFTER INSERT
ON insert_code_table
FOR EACH ROW
BEGIN
DELETE FROM reward_code_table WHERE reward_code = NEW.customer_code;
END;
//
DELIMITER ;
INSERT INTO insert_code_table(customer_code, another_random_col)
VALUES (2, "del 2 from reward table");
After inserting into one table, it deletes record from the other table.
You can checkout a sample SQLFiddle. Note that I have kept the delimiter as // in the fiddle example
Also consider using prepared statements to prevent basic mysql injections.

How to get IDs for bulk inserts in codeigniter?

In Codeigniter, if I create an SQL string which does multiple inserts, how do I get the inserted id of each?
// Prepare the SQL
$sql = '';
$chunk = array(array(), array(), array()); // The elements are arrays
foreach($chunk as $arr){
// The first field is the primary key (INT NOT NULL auto_increment)
$sql .= "(NULL, {$arr[0]}, {$arr[1]}, {$arr[2]}, {$arr[3]}, {$arr[4]})";
if($arr!= $last) $sql .= ', ';
}
// Start inserting into the db
$this->db->trans_start();
$this->db->query('INSERT INTO my_table VALUES '.$sql);
// A few other queries go here which need the IDs of the previous insert
$this->db->trans_complete();
This is also my first time using transactions.
You probably need to record the MAX id before the insert, and then select all the ids greater than that after the bulk insert.
EDIT:
I found this in the MySQL manual:
The correct way to use LOCK TABLES and UNLOCK TABLES with transactional tables, such as InnoDB tables, is to begin a transaction with SET autocommit = 0 (not START TRANSACTION) followed by LOCK TABLES, and to not call UNLOCK TABLES until you commit the transaction explicitly.
I decided to go with using transactions instead.

How to retrieve scalar value from stored procedure (ADO.NET)

If in the stored procedure, I just execute one statement, select count(*) from sometable, then from client side (I am using C# ADO.Net SqlCommand to invoke the stored procedure), how could I retrieve the count(*) value? I am using SQL Server 2008.
I am confused because count(*) is not used as a return value parameter of stored procedure.
thanks in advance,
George
Either you use ExecuteScalar as Andrew suggested - or you'll have to change your code a little bit:
CREATE PROCEDURE dbo.CountRowsInTable(#RowCount INT OUTPUT)
AS BEGIN
SELECT
#RowCount = COUNT(*)
FROM
SomeTable
END
and then use this ADO.NET call to retrieve the value:
using(SqlCommand cmdGetCount = new SqlCommand("dbo.CountRowsInTable", sqlConnection))
{
cmdGetCount.CommandType = CommandType.StoredProcedure;
cmdGetCount.Parameters.Add("#RowCount", SqlDbType.Int).Direction = ParameterDirection.Output;
sqlConnection.Open();
cmdGetCount.ExecuteNonQuery();
int rowCount = Convert.ToInt32(cmdGetCount.Parameters["#RowCount"].Value);
sqlConnection.Close();
}
Marc
PS: but in this concrete example, I guess the alternative with just executing ExecuteScalar is simpler and easier to understand. This method might work OK, if you need to return more than a single value (e.g. counts from several tables or such).
When you execute the query call ExecuteScalar - this will return the result.
Executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored.
Since you are only returning one value this would return just the value from your count expression. You will need to cast the result of this method to an int.
marc_s answer worked fine for integer. but for varchar the lenght must be specifed.
cmdGetCount.Parameters.Add("#RowCount", SqlDbType.varchar,30).Direction = ParameterDirection.Output;