Microsoft Access - stored procedure with cursor - ms-access

I have a stored procedure in SQL Server that I am using to delete duplicates from one of the tables.
This stored procedure makes use of a cursor.
I tried to create the same stored procedure in Microsoft Access by just replacing the 'CREATE PROCEDURE' with 'CREATE PROC' but it didn't seem to work.
Can anyone provide some workaround?
Here is the SQL Server stored procedure:
ALTER PROCEDURE [dbo].[csp_loginfo_duplicates]
AS
BEGIN
SET NOCOUNT ON;
declare #minrowid bigint
declare #empid nvarchar(15)
declare #dtpunched datetime
declare #count tinyint
declare curDuplicate cursor for
select empid,dtpunched,count(*),min(row_id) from loginfo
group by empid,dtpunched
having count(*)>1
open curDuplicate
fetch next from curduplicate into #empid,#dtpunched,#count,#minrowid
while (##fetch_status=0)
begin
delete from loginfo where empid=#empid and dtpunched=#dtpunched and row_id<>#minrowid
fetch next from curduplicate into #empid,#dtpunched,#count,#minrowid
end
close curDuplicate
deallocate curDuplicate
END

Beginning with Jet 4 (Access 2000), Access DDL includes support for CREATE PROCEDURE when you execute the DDL statement from ADO. However, those "procedures" are limited compared to what you may expect based on your SQL Server experience. They can include only one SQL statement, and no procedural type code. Basically an Access stored procedure is the same as a saved query, and is therefore subject to the same limitations which include the fact that Access' db engine doesn't speak T-SQL.
As for a workaround, you could create an Access-compatible DELETE statement to remove the duplicates. But I would first look for "the simplest thing which could possibly work".
Run your stored procedure on SQL Server to remove the duplicates.
Create a unique constraint to prevent new duplicates of the empid/dtpunched pairs in your loginfo table.
In your Access application, create a link to the SQL Server loginfo table.
However, I have no idea whether that suggestion is appropriate for your situation. If you give us more information to work with, chances are you will get a better answer.

Related

how to cross check all the tables in MYSQL database [duplicate]

Is there a mySQL query to search all tables within a database?
If not can you search all tables within a database from the mySQL workbench GUI?
From phpmyadmin there's a search panel you can use to select all tables to search through. I find this super effective since magento, the ecommerce package I'm working with has hundreds of tables and different product details are in different tables.
Alternatively, if your database is not that huge, you can make a dump and make your search in the .sql generated file.
If you want to do it purely in MySQL, without the help of any programming language, you could use this:
## Table for storing resultant output
CREATE TABLE `temp_details` (
`t_schema` varchar(45) NOT NULL,
`t_table` varchar(45) NOT NULL,
`t_field` varchar(45) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
## Procedure for search in all fields of all databases
DELIMITER $$
#Script to loop through all tables using Information_Schema
DROP PROCEDURE IF EXISTS get_table $$
CREATE PROCEDURE get_table(in_search varchar(50))
READS SQL DATA
BEGIN
DECLARE trunc_cmd VARCHAR(50);
DECLARE search_string VARCHAR(250);
DECLARE db,tbl,clmn CHAR(50);
DECLARE done INT DEFAULT 0;
DECLARE COUNTER INT;
DECLARE table_cur CURSOR FOR
SELECT concat('SELECT COUNT(*) INTO #CNT_VALUE FROM `',table_schema,'`.`',table_name,'` WHERE `', column_name,'` REGEXP ''',in_search,''';')
,table_schema,table_name,column_name
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA NOT IN ('information_schema','test','mysql');
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1;
#Truncating table for refill the data for new search.
PREPARE trunc_cmd FROM "TRUNCATE TABLE temp_details;";
EXECUTE trunc_cmd ;
OPEN table_cur;
table_loop:LOOP
FETCH table_cur INTO search_string,db,tbl,clmn;
#Executing the search
SET #search_string = search_string;
SELECT search_string;
PREPARE search_string FROM #search_string;
EXECUTE search_string;
SET COUNTER = #CNT_VALUE;
SELECT COUNTER;
IF COUNTER>0 THEN
# Inserting required results from search to table
INSERT INTO temp_details VALUES(db,tbl,clmn);
END IF;
IF done=1 THEN
LEAVE table_loop;
END IF;
END LOOP;
CLOSE table_cur;
#Finally Show Results
SELECT * FROM temp_details;
END $$
DELIMITER ;
Source: http://forge.mysql.com/tools/tool.php?id=232
If you are using MySQL Workbench, you can do this by doing right click on the DB Schema you want to search into, and then "Search Table Data...".
In there you can select the "Search using REXEXP" option, and then type your text of search as usual. It will provide the DB rows matching your specific text.
You will need to check the "Search columns of all types" box as well.
In MySQL Workbench you can use the Table Data Search feature. It can search across multiple tables and/or multiple databases.
Search string in all tables on a database is a complex task. Normally you don't need to use exactly all tables and results are complex to read without a specific layout (tree of tables with matches or the like)
SQL Workbench/J offers a GUI and a command-line version to do such task:
More info:
http://www.sql-workbench.net/manual/wb-commands.html#command-search-data
http://www.sql-workbench.net/manual/dbexplorer.html#search-table-data
NOTE: Search with JDBC driver uses a lot of memory if it is not configured properly. SQL Workbench/J warns about that and although online documentation is a bit outdated, the sources of documentation (doc/xml/db-problems.xml) explain how to fix it for different BBDD:
Here an extract for Postgres:
The PostgreSQL JDBC driver defaults to buffer the results obtained from the database
in memory before returning them to the application. This means that when retrieving data, &wb-productname; uses (for a short amount of time) twice as much memory as really needed. This also means that WbExport or WbCopy will
effectively read the entire result into memory before writing it into the output file.
For large exports, this is usually not wanted.
This behavior of the driver can be changed so that the driver uses cursor based retrieval.
To do this, the connection profile must disable the "Autocommit" option and must define a default fetch size that is greater than zero. A recommended value is e.g. 10, it might be that higher numbers give a better performance. The number defined for the fetch size,
defines the number of rows the driver keeps in its internal buffer before requesting more
rows from the backend.

SQL error when I try to ALTER TABLE in a stored procedure

I have a stored procedure designed to generate a new, 'derived' table. In this procedure I then want to add a column using ALTER TABLE. However, despite an almost identical stored procedure working fine, and despite being able to add this manually as a stored procedure to the database using MySQL Workbench, when I pass the code to the server using SOURCE (i.e. SOURCE workload.sql), I get an error 1146 (42502) 'Table 'workload._convenor_workload' doesn't exist.' (I'm doing this in Emacs as part of a org-babel block, but this is essentially just passing raw SQL to the server.)
As background, I'm in the process of migrating SQL code from a setting where I was running it raw to create my final database to one where I'd like this code to be called via triggers.
Setup: mysql Ver 8.0.16 for macos10.14 on x86_64 (MySQL Community Server - GPL)
I've tried rewriting this as a prepared statement, was unsuccessful, and have been scouring Stack Overflow. This is my first MySQL project and my reading of the documentation suggests that ALTER TABLE is a perfectly legal thing to do in a stored procedure. It's likely that I'm making a schoolboy error somewhere but at the moment I'm banging my head.
Elsewhere in my SQL, this code works in a stored procedure (ALTER TABLE function does not throw an error):
CREATE TABLE _assessment_allocations AS SELECT Assessment_ID,
IFNULL(SUM(_total_first_marking_hours),0) AS _total_first_marking_hours_sum,
GROUP_CONCAT(DISTINCT _total_first_marking_hours_needed) AS _total_first_marking_hours_needed,
GROUP_CONCAT(DISTINCT Prog_ID) AS prog_id
FROM
_marking_workload
GROUP BY Prog_ID, Assessment_ID;
ALTER TABLE _assessment_allocations
ADD COLUMN _assessment_variance DECIMAL(5,2);
However, the code that throws the error is this (specifically, the ALTER TABLE function; I've added the stored procedure code in case this is helpful). Note that this code does not throw an error when ingested by MySQL outside a stored procedure:
USE `workload`;
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `module_administration_convenor`()
-- Begin by selecting elements of the ~modules~ table
CREATE TABLE `_convenor_workload` AS
SELECT Modules.Module_Code,
Modules.Module_Name,
Modules.Module_Convenor_ID,
Modules.Module_Convenor_Share,
Modules.Student_Tally,
Modules.Additional_Hours,
Modules.Convening_Notes,
Modules.Active_Status
FROM modules;
-- Add a 'Convenor' column
ALTER TABLE `_convenor_workload` ADD COLUMN `Name` VARCHAR(255) DEFAULT 'Convenor';
\* Other stuff *\
END$$
DELIMITER ;
My aim is to avoid throwing this error. I'd like to get this stored procedure actually stored! (Just like the previous stored procedure that does much the same and does not throw an error.) I'm aware that there are some back-tick and style differences between the working and non-working code, but I'm guessing these aren't super important.
As I said, I have a strong suspicion that I'm overlooking something obvious here...
As mentioned by Solarflare in the comments, you are missing a begin so the alter table is executing as a separate action. If you wrap it with begin and end then it treats all the code as the stored procedure.
USE `workload`;
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `module_administration_convenor`()
Begin
-- Begin by selecting elements of the ~modules~ table
CREATE TABLE `_convenor_workload` AS
SELECT Modules.Module_Code,
Modules.Module_Name,
Modules.Module_Convenor_ID,
Modules.Module_Convenor_Share,
Modules.Student_Tally,
Modules.Additional_Hours,
Modules.Convening_Notes,
Modules.Active_Status
FROM modules;
-- Add a 'Convenor' column
ALTER TABLE `_convenor_workload` ADD COLUMN `Name` VARCHAR(255) DEFAULT 'Convenor';
END
$$
DELIMITER ;

How to synchronize access to user variables during single session?

I need to use user variable in prepare statement of mysql
stored procedure. (The purpose is to substitute the table name in
drop table command, which is unable to be injected via ? and
passed via execute using... statement because it is not a data element).
I suppose the user variables are session-wide global variables.
I suppose the stored procedure accessing the user variable must be
synchronized to protect against unwanted behaviour when it is called
simultaneously more times within single session (which I cannot prevent).
How to perform such synchronization?
Is there any chance it is performed internally by the mysql?
It seems like mysql get_lock() & co. uses logic that does not help much:
get_lock('a') followed by get_lock('b') destroys state of a. Maybe I have terribly missed some point here...
For those who would ask the "what exactly would you like to do" question:
drop procedure if exists drop_t_table; delimiter $$
create procedure drop_t_table(in in_t_table_name varchar(128))
begin
declare sql_drop varchar(256) default 'drop temporary table if exists ';
--
-- I would suspect sql_drop_table user variable guard should be locked here...
--
set #sql_drop_table = concat(sql_drop, in_t_table_name);
--
-- What if the procedure is preempted to another call here
-- and the sql_drop_table gets different table name?
--
prepare exe from #sql_drop_table;
--
-- ...and unlocked here
--
execute exe;
deallocate prepare exe;
end$$ delimiter ;
Variables declared within your stored procedure are local to the procedure. An example is sql_drop in your code.
Each session is, basically, single-threaded. You can't do more than one thing at a time within a session. There's no way to call a stored procedure more than once from within a particular session.
If you have more than one session you can call the same stored procedure from both of them. But, a DROP TABLE operation is basically idempotent: If you call it more than once, it has the same effect as calling it just once. It isn't precisely idempotent: it throws an error if the table doesn't exist. But, still, dropping the same table more than once isn't any more destructive than dropping it once.
Temporary tables (a) are only visible to the session that created them, and (b) vanish when the session ends. So, going to a lot of trouble to drop them explicitly might be overkill.
So, with respect, you might be overthinking this problem.

Create a stored procedure only if the procedure does not exist in mysql

In SQL Server I am able to achieve this using dynamic sql string, but now I need to do the same thing for mysql but am getting nowhere, is there any way to achive this
IF NOT EXISTS (SELECT 1 FROM mysql.proc p WHERE NAME = 'stored_proc_name')
BEGIN
DELIMITER $$
CREATE PROCEDURE justATest()
BEGIN
-- some SP logic here
END$$
END
I am storing the whole sql as a string inside a database column and execute the statement using a prepared statement Execute inside another stored procedure.
IF NOT EXISTS(SELECT 1 FROM mysql.proc p WHERE db = 'db_name' AND name = 'stored_proc_name') THEN
....
taken from
Older Post
Control statements like if then else are only allowed inside Stored Procedures in MySQL (unfortunately). There are usually ways around this, but it depends why you are conditionally creating the sproc.
E.g. If you're trying to avoid errors when running build scripts because sprocs already exist then you can use a conditional drop statement prior to your create like this:
DROP PROCEDURE IF EXISTS justATest;
CREATE PROCEDURE justATest()
BEGIN
-- enter code here
END;
This will ensure the any changed code gets run (rather than skipped).

Building and Testing with MySQL Workbench for SSMS People

I am great with Microsoft's SQL Server and SQL Server Management Studio (SSMS).
I'm trying to get things I used to do there to work in MySQL Workbench, but it is giving me very unhelpful errors.
Currently, I am trying to write an INSERT statement. I want to declare my variables and test it with a few values, then turn the end result into a stored procedure.
Right now, I have a syntax error that is not allowing me to continue, and the error message is not helpful either:
syntax error, unexpected DECLARE_SYM
There is no error list at the bottom and no way to copy the text of that error to the clipboard, so it all has to be studied on one screen, then flip to this screen so I can write it down.
Irritating!
The MySQL documentation surely has what I'm looking for, but I can learn much faster by doing than spending weeks reading their online manual.
DELIMITER $$
declare cGroupID char(6) DEFAULT = 'ABC123';
declare subGroupRecords int;
declare nDocTypeID int(11);
declare bDocActive tinyint(1) DEFAULT '1';
declare cDocID varchar(256) DEFAULT NULL;
insert into dbo_connection.documents
(group_id, subgroup_id, type_id, active, title, doc_id, priority, ahref, description, last_modified)
values
(cGroupID,cSubGroupID,nDocTypeID,bDocActive,cTitle,cDocID,0,ahref1, docDesc,NOW());
select * from dbo_connection.documents where group_id='ABC123';
END
So, for right now, I'm looking for why MySQL does not like my declare statement.
For the long term, I'm interested in finding a short article that shows a cookbook approach to doing some of the basic tasks that SQL developers would need (i.e. skips the Hello World program and discussion on data types).
DECLARE is only valid within stored programs. In other words, unlike T-SQL, you can't build up your query and then wrap CREATE PROCEDURE around it to turn the end result into a stored procedure, you have to build it up as a stored procedure from the get-go.