I just want to run this type of procedure from Qt side:
DROP PROCEDURE IF EXISTS get_patient_info_brief;
DELIMITER //
CREATE PROCEDURE get_patient_info_brief(IN _id int)
BEGIN
SELECT age, height, weight, sessions, remaining_sessions, `description` FROM patient_info WHERE `id` = _id;
END //
DELIMITER ;
like this:
const auto q1 = QString("CALL get_patient_info_brief(?);");
const auto q2 = QStringLiteral("SELECT age, height, weight, sessions, remaining_sessions, `description` FROM patient_info WHERE `id` = ?;");
auto db = QSqlDatabase::addDatabase("QMYSQL");
db.setDatabaseName("mydb");
db.setUserName("root");
db.setPassword("pswd");
if (db.open()) {
QSqlQuery q(db);
q.prepare(q2);
q.addBindValue(QVariant(1));
if (q.exec()) {
qDebug() << "query executed";
if (q.size()) {
while (q.next()) {
qDebug() << q.record();
}
}
}
}
if I use the q2 the result will be returned back but it's not true with q1
Docs would say:
MySQL 5 introduces stored procedure support at the SQL level, but no API to
control IN, OUT, and INOUT parameters. Therefore, parameters have to
be set and read using SQL commands instead of QSqlQuery::bindValue().
How can we get manage to do the job with q1 one?
I'm using Qt5.8 + MySQL 8.0.17 + MySQL Connector C 6.1
Using PyQt5 there are two ways to call the stored procedure in PyQt5 and both
work. I guess these will also work in Qt even though I haven't tried it.
q.exec("CALL get_patient_info-brief ({})".format(1)
or
q.prepare("CALL get_patient_info-brief (:id)")
q.bindValue(":id", 1)
q.exec()
Both work pretty well for me. I understand that when using positional
placeholders using named
placeholders it's preferable.
Related
I need to write a test for some download operation. This operation call procedure from MSSQL database, take result set and java make some stuf. For test I use hsqldb.
My procedure:
CREATE PROCEDURE map.Get1(IN packageName varchar(100),
IN downloadDate DATE)
READS SQL DATA DYNAMIC RESULT SETS 1 BEGIN ATOMIC
DECLARE result CURSOR WITH RETURN FOR SELECT * FROM map.tvschedule FOR READ ONLY;
OPEN result;
END
This procedure wan't work, i have an exception
call map.GET1('Genre','2018-03-10');
[42561][-5561] incompatible data type in conversion
java.lang.RuntimeException: org.hsqldb.HsqlException: incompatible data type
in conversion
But this(without date parameter) work well:
CREATE PROCEDURE map.Get1(IN packageName varchar(100))
READS SQL DATA DYNAMIC RESULT SETS 1 BEGIN ATOMIC
DECLARE result CURSOR WITH RETURN FOR SELECT * FROM map.tvschedule FOR READ ONLY;
OPEN result;
END
call map.GET1('Genre');
first needed row
second needed row
I am not going to use input parameter, but i need this procedure to be looking i am going to.
My question is How to use date input parameter with hsqldb procedures?
UPDATE1:
I used TO_DATE and now it works well, but i have no data in my result set, my java code is:
try (CallableStatement callableStatement = connection.prepareCall("{ call
map.GetGenreProtocol( ?, ? ) }")) {
callableStatement.setString(1, packageName);
callableStatement.setDate(2, date);
callableStatement.execute();
ResultSet resultSet = callableStatement.getResultSet();
while (resultSet.next()) {
Interval Interval = new Interval();
Interval.setDuration(resultSet.getInt("duration"));
Interval.setMappingTargetId(resultSet.getInt("mappingTargetId"));
Interval.setGenreId(resultSet.getInt("genreId"));
Interval.setStart(resultSet.getLong("start"));
Interval.setCategoryId(resultSet.getInt("categoryId"));
Interval.setCategoryName(resultSet.getString("categoryName"));
Interval.setGenreName(resultSet.getString("genreName"));
Interval.setDescription(resultSet.getString("description"));
Intervals.add(Interval);
}
}
Use the TO_DATE function.
For example:
call map.GET1('Genre', TO_DATE('2018-03-10', 'YYYY-MM-DD'));
I guess you need to create a function that returns a table instead of a procedure:
CREATE FUNCTION map.Get1(IN packageName VARCHAR(100),
IN downloadDate DATE)
RETURNS TABLE(.....)
READS SQL DATA
BEGIN ATOMIC
....
END;
I have the following code in C# that I would like to turn into a MySQL snippet instead?
foreach (DataRow row in dtBills.Rows)
{
classes.UtilityBill ub = new classes.UtilityBill(row);
if (ub.ApprovedBy > 0)
{
if (ub.RemainingBalance() > 0) { totalOutstanding += ub.RemainingBalance(); numberOfUnpaidBills++; }
if (ub.RemainingBalance() > 0 && ub.IsOverDue()) { numberOfOverdueBills++; }
}
else
{
if (ub.ApprovedBy == 0)
{
awaitingApproval++;
}
else
{
rejectedBills++;
}
}
}
ub.RemainingBlaance() basically checks two fields in the table and subtracts, amount_due - amount_paid
What I'm looking to have returned is a single row that looks something like:
totalOutstanding, numberOfUnpaidBills, numberOfOverdueBills, awaitingApproval, rejectedBills
Original table:
I know that this might come across like a lazy question, but I have never dealt with coding in mysql, just simple queries, and I have no idea on how to start. A push in the right direction would suffice, not a complete solution.
Thanks!
To query mysql you can use the mysql.data nuget package. You can look up their documentation. I created a nuget to simplify code from any c# framework to mysql as well. If you don't mind third party nugets, you can try it out. It is called mysql.simple. From what I understand from what the code is trying to do, here is a small code for pulling data:
using (Database db = ConnectionString) // initializes connection
{
// you can omit the below select function if you would like to select all
using (var Row = db.Select("unit_cost, start_reading")
From("tblName").Query()
while(Row.Read()){ // gets each row
ub.unit_cost = Row["unit_cost"];
ub.start_reading= Row["start_reading"];
.
.
.
}
}
The db object above has select, update and insert functions with all variations including insert statements with selects. However if you would like to use raw sql queries, you can directly use db.Query("sql query here"). You can see more documentations here http://mysqlsimple.azurewebsites.net/docs
Here is a sample mySql stored procedure I had with me:
DELIMITER //
DROP PROCEDURE IF EXISTS proc_IF;
CREATE PROCEDURE proc_IF (IN param1 INT)
BEGIN
DECLARE variable1 INT;
SET variable1 = param1 + 1;
IF variable1 = 0 THEN
SELECT variable1;
END IF;
IF param1 = 0 THEN
SELECT 'Parameter value = 0' ;
ELSE
SELECT 'Parameter value <> 0' ;
END IF;
END //
This example contains parameters, variables and if statement examples.
You can call this in c# something like this:
db.QueryValue("CALL proc_IF (#0)", myVal);
Not sure where the problem is here... I have a stored procedure written that (when executed within SQL Server Mgmt. Studio) returns results I expect to see:
USE [DataViewer]
GO
/****** Object: StoredProcedure [dbo].[DV_GetPSCTreeNodes] Script Date: 08/10/2014 1:48:03 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <Me!>
-- Description: <Used by data viewer GUI.>
-- =============================================
ALTER PROCEDURE [dbo].[DV_GetPSCTreeNodes]
-- Add the parameters for the stored procedure here
#iNodeLevel int,
#iParentNodeId bigint
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sQuery varchar (2000)
IF #iNodeLevel >= 5
RETURN
ELSE
-- Insert statements for procedure here
SET #sQuery = 'SELECT * FROM DataView.dbo.v_Tree
WHERE L' + CAST((#iNodeLevel + 1) AS VARCHAR(10)) + 'ID IS NULL
AND L' + CAST((#iNodeLevel) AS VARCHAR(10)) + 'ID = ' + CAST((#iParentNodeId)
AS VARCHAR(10))
EXEC (#sQuery)
END
As mentioned, if I run this in SMSS, I get the expected results.
However, if I run this through the service I have set up, I get the correct amount of records back, but they are all duplicates of the first row (and I can look at the table and know I am getting duplicates). If I try new params and get 15 distinct rows back in SSMS, my webpage will show 15 rows of repeated row 1 data.
Here is the call from my Client:
List<v_PowerSystemCIMTree> list = null;
DVServiceClient proxy = new DVServiceClient();
try
{
list = proxy.DV_GetPSCTreeNodes(2,325550).ToList(); //2 and 325550 are just hardcoded to
//check to see if it's working
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType().FullName);
Console.WriteLine(ex.Message);
}
finally
{
proxy.Close();
}
return View(list);
Which calls my Service:
readonly DataViewerEntities _Context = new DataViewerEntities();
public List<DataViewer_DAL.v_PowerSystemCIMTree> DV_GetPSCTreeNodes(int firstParam,
long secondParam)
{
return _Context.DV_GetPSCTreeNodes(firstParam,secondParam).ToList();
}
Why the heck would I get one result set in the Management Studio, but repeated data on my webpage?
Known issue: since you're selecting from a view which doesn't have a defined primary key, Entity Framework will use all non-nullable columns as the "entity key".
If EF reads a second row from the view that has the same entity key (same values in all non-nullable columns), it will just repeat the first row already read - it will not look at the other, remaining columns....
So you just need to either add the primary key for the underlying table(s) to the view's columns, tweak the EF data model to explicitly define an "entity key" for the view, or you need to add an artificial, unique column (like a ROW_NUMBER()) to your view and make sure it's non-nullable and thus part of the EF "derived" entity key
I have this form in C# with a listbox where I selected 4 items. Now I want to make single stored procedure using which I can find data from single table for all this selected item with single parameter.
As I am a beginner when it comes to SQL Server, I completely don't know this type of procedure
Thanks, but this is not my question's answer
I want a Single Stored Procedure for all Items which are selected in ListBox
Create Procedure procedureName
(
#ItemName varchar(50),
)
AS
BEGIN
(
Select * from item_master where item_name = #ItemName
)
END
by this Query i can find data for one ItemName, but i want for all selected Items in Listbox, even I don't know the C# code also,
so plz help me....
This is a very simple example that does what you want. You would not want to use hard-coded connection strings, especially in-line, and you would want error-handling, but I am going for as much clarity as possible. You would also probably want to make the column length greater than 50 characters, but I made it match your column definition.
Also, I would recommend a generic approach, passing keys (column names) and values, so as to be able to use it for any sort of criteria, but you asked that I keep it to exactly what you require, so I trimmed it down to the essential.
This example returns all the Employees with FirstName matching any in the list passed to the stored procedure (as a user-defined table type).
First, create a user-defined table type (to hold the values you want to pass to the stored procedure) in your SQL Server database as follows:
CREATE TYPE [dbo].[FilterValues] AS TABLE(
[Value] [varchar](50) NOT NULL,
PRIMARY KEY CLUSTERED
(
[Value] ASC
)
)
The stored procedure to return the Employees looks as follows (note that it has the user-defined table type as the type of the single parameter passed in):
CREATE PROCEDURE [dbo].[GetEmployees] (
#FirstNameFilterValues dbo.FilterValues READONLY
)
AS
BEGIN
SELECT * FROM Employees
INNER JOIN #FirstNameFilterValues fv ON fv.Value = Employees.FirstName;
END
That's the SQL Server side done. To call it from C#, you can create a DataTable with a single column matching the column name and populate it with the values you want. In this simple example, I populate it with two names, but it could be as many as you want.
var filterValuesDataTable = new DataTable();
filterValuesDataTable.Columns.Add(new DataColumn("Value", typeof(string)) { AllowDBNull = false });
filterValuesDataTable.Rows.Add("Frodo");
filterValuesDataTable.Rows.Add("Sam");
using (var connection = new SqlConnection("server=.;Initial Catalog=Test;Integrated Security=True;"))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = "GetEmployees";
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("#FirstNameFilterValues", filterValuesDataTable);
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine("{0} {1}", reader["FirstName"], reader["LastName"]);
}
reader.Close();
}
}
connection.Close();
}
Is there a case insensitive Replace for MySQL?
I'm trying to replace a user's old username with their new one within a paragraph text.
$targetuserold = "#".$mynewusername;
$targetusernew = "#".$newusername;
$sql = "
UPDATE timeline
SET message = Replace(message,'".$targetuserold."', '".$targetusernew."')
";
$result = mysql_query($sql);
This is missing the instances where the old username is a different case. Example: replacing "Hank" with "Jack" in all the rows in my database will leave behind instances of "hank".
An easier way that works without any stored function:
SELECT message,
substring(comments,position(lower('".$targetuserold."') in message) ) AS oldval
FROM timeline
WHERE message LIKE '%".$targetuserold."%'
gives you the exact, case sensitive spellings of the username in all messages. As you seem to run that from a PHP script, you could use that to collect the spellings together with the corresponding IDs, and then run a simple REPLACE(message,'".$oldval.",'".$targetusernew."') on that. Or use the above as sub-select:
UPDATE timeline
SET message = REPLACE(
message,
(SELECT substring(comments,position(lower('".$targetuserold."') in message))),
'".$targetusernew."'
)
Works like a charm here.
Credits given to this article, where I got the idea from.
Here it is:
DELIMITER $$
DROP FUNCTION IF EXISTS `replace_ci`$$
CREATE FUNCTION `replace_ci` ( str TEXT,needle CHAR(255),str_rep CHAR(255))
RETURNS TEXT
DETERMINISTIC
BEGIN
DECLARE return_str TEXT DEFAULT '';
DECLARE lower_str TEXT;
DECLARE lower_needle TEXT;
DECLARE pos INT DEFAULT 1;
DECLARE old_pos INT DEFAULT 1;
SELECT lower(str) INTO lower_str;
SELECT lower(needle) INTO lower_needle;
SELECT locate(lower_needle, lower_str, pos) INTO pos;
WHILE pos > 0 DO
SELECT concat(return_str, substr(str, old_pos, pos-old_pos), str_rep) INTO return_str;
SELECT pos + char_length(needle) INTO pos;
SELECT pos INTO old_pos;
SELECT locate(lower_needle, lower_str, pos) INTO pos;
END WHILE;
SELECT concat(return_str, substr(str, old_pos, char_length(str))) INTO return_str;
RETURN return_str;
END$$
DELIMITER ;
Usage:
$sql = "
UPDATE timeline
SET message = replace_ci(message,'".$targetuserold."', '".$targetusernew."')
";
My solution ultimately was that I cannot do a case insensitive Replace.
However, I did find a workaround.
I was trying to have a feature where a user can change their username. The system would then need to update wherever #oldusername was found in all the messages in the database.
The problem was... people wouldn't type other people's usernames in the correct case that it is found in the members table. So when the user would change their username, it wouldn't catch those instances of #oldSeRNAmE because of it not matching the case of the real format of the oldusername.
I don't have permission with my GoDaddy shared server to do this with a customized SQL function, so I had to find a different way.
My solution: Upon inserting new messages into the database, whenever a username is found in the new message, I have an UPDATE statement at that point to replace the username they typed with the correct formatted case that is found in the members table. That way, if that person ever wants to change their username in the future, all the instances of that username in the database will all be the same exact formatted case. Problem solved.