Exporting Blob from MySQL database to file with only SQL - sql

I have a table with image data stored in a blob field in a MySQL database. Is there a way to export those images to files on the filesystem by using only SQL? The images should be named {imageId}.jpg
I know that it is easy to do this with Java or whatever but is it possible with just a SQL script?

Using INTO, and assuming you have write permission as the mysql user in the location you wish to store the files, you can do:
SELECT id, blob INTO DUMPFILE '/tmp/path' FROM table;
Unfortunately, in MySQL it is not possible to specify the dumpfile as an expression/variable. However, you could achieve this if you wrapped it in a stored procedure and use variables.

I don't like the idea ...
drop procedure if exists dump_image;
delimiter //
create procedure dump_image()
begin
declare this_id int;
declare cur1 cursor for select imageId from image;
open cur1;
read_loop: loop
fetch cur1 into this_id;
set #query = concat('select blob_field from image where imageId=',
this_id, ' into outfile "/tmp/xyz-', this_id,'.jpg"');
prepare write_file from #query;
execute write_file;
end loop;
close cur1;
end //
delimiter ;
Despite the error
mysql> call dump_image();
ERROR 1329 (02000): No data - zero rows fetched, selected, or processed
ls -1 /tmp/xyz*

Related

how can I print results from sql procedure?

I'm writing a procedure to count rows in every table in my database. It so far looks like this:
create or replace procedure count_database_rows()
dynamic result sets 1
P1: begin atomic
DECLARE stmt CHAR(40);--
FOR v1 AS
c1 CURSOR FOR
SELECT TABLE_SCHEMA, TABLE_NAME FROM sysibm.tables
DO
SET stmt = 'SELECT COUNT(*) FROM '||TABLE_SCHEMA||'.'||TABLE_NAME;--
PREPARE s FROM stmt;--
EXECUTE s;--
END FOR;--
end P1
~
however, when I run it:
db2 -ntd~ -f script.sql > dump.csv
all I'm getting is:
DB20000I The SQL command completed successfully.
how can I print all results instead?
Just for demonstration. I assume, that it's some educational task, and it's Db2 for LUW.
For non-DPF Db2 for LUW systems only
--#SET TERMINATOR #
CREATE OR REPLACE FUNCTION COUNT_DATABASE_ROWS()
RETURNS TABLE (P_TABSCHEMA VARCHAR(128), P_TABNAME VARCHAR(128), P_ROWS BIGINT)
BEGIN
DECLARE L_STMT VARCHAR(256);
DECLARE L_ROWS BIGINT;
FOR V1 AS
SELECT TABSCHEMA, TABNAME
FROM SYSCAT.TABLES
WHERE TYPE IN ('T', 'S')
FETCH FIRST 10 ROWS ONLY
DO
SET L_STMT = 'SET ? = (SELECT COUNT(*) FROM "'||V1.TABSCHEMA||'"."'||V1.TABNAME||'")';
PREPARE S FROM L_STMT;
EXECUTE S INTO L_ROWS;
PIPE(V1.TABSCHEMA, V1.TABNAME, L_ROWS);
END FOR;
RETURN;
END#
SELECT * FROM TABLE(COUNT_DATABASE_ROWS())#
For any Db2 for LUW systems
A little bit tricky for DPF systems, but doable as well. We have to wrap the code which is not allowed in the inlined compound statement into the stored procedure.
--#SET TERMINATOR #
CREATE OR REPLACE PROCEDURE COUNT_DATABASE_ROWS_DPF(OUT P_DOC XML)
READS SQL DATA
BEGIN
DECLARE L_STMT VARCHAR(256);
DECLARE L_ROWS BIGINT;
DECLARE L_NODE XML;
SET P_DOC = XMLELEMENT(NAME "DOC");
FOR V1 AS
SELECT TABSCHEMA, TABNAME
FROM SYSCAT.TABLES
WHERE TYPE IN ('T', 'S')
FETCH FIRST 10 ROWS ONLY
DO
SET L_STMT = 'SET ? = (SELECT COUNT(*) FROM "'||V1.TABSCHEMA||'"."'||V1.TABNAME||'")';
PREPARE S FROM L_STMT;
EXECUTE S INTO L_ROWS;
SET L_NODE = XMLELEMENT
(
NAME "NODE"
, XMLELEMENT(NAME "TABSCHEMA", V1.TABSCHEMA)
, XMLELEMENT(NAME "TABNAME", V1.TABNAME)
, XMLELEMENT(NAME "ROWS", L_ROWS)
);
SET P_DOC = XMLQUERY
(
'transform copy $mydoc := $doc modify do insert $node as last into $mydoc return $mydoc'
passing P_DOC as "doc", L_NODE as "node"
);
END FOR;
END#
CREATE OR REPLACE FUNCTION COUNT_DATABASE_ROWS_DPF()
RETURNS TABLE (P_TABSCHEMA VARCHAR(128), P_TABNAME VARCHAR(128), P_ROWS BIGINT)
BEGIN ATOMIC
DECLARE L_DOC XML;
CALL COUNT_DATABASE_ROWS_DPF(L_DOC);
RETURN
SELECT *
FROM XMLTABLE ('$D/NODE' PASSING L_DOC AS "D" COLUMNS
TYPESCHEMA VARCHAR(128) PATH 'TABSCHEMA'
, TABNAME VARCHAR(128) PATH 'TABNAME'
, LENGTH BIGINT PATH 'ROWS'
);
END#
-- Usage. Either CALL or SELECT:
CALL COUNT_DATABASE_ROWS_DPF(?)#
SELECT * FROM TABLE(COUNT_DATABASE_ROWS_DPF())#
If your Db2-server runs on Linux/Unix/Windows then you can use the DBMS_OUT.PUT_LINE function to send diagnostic output from SQL routines to the console. The idea is that in your routine, you assign to a variable some text (example, the table name and its count), then call DBMS_OUTPUT.PUT_LINE(...) to cause that text to appear on the console. The disadvantage of this approach is that the output will only appear once the routine has completed. This is often not what you want, sometimes you want to see the row-counts as they become available, so consider instead alternative approaches, as shown below.
To see DBMS_OUTPUT.PUT_LINE output with the Db2 CLP (or db2cmd.exe) you first need to use set serveroutput on before calling the procedure.
But for simple stuff like this, a stored procedure might be unsuitable, because you can use the CLP to do the work in two steps after connecting to the database. This is often more convenient for scripting purposes. The idea is that you make a file to generate the queries, which when you run with CLP creates a second file, and you execute the second file to get the desired results.
Example
Create file gen_counts.sql containing the query that generates the real queries, for example gen_counts.sql might contain
select 'select count(*) from '||rtrim(tabschema)||'.'||rtrim(tabname)||' with ur;'
from syscat.tables;
Then you can do these steps:
db2 connect to $database
db2 -txf gen_counts.sql > count_queries.sql
db2 -tvf count_queries.sql > count_results.txt
Note that the output file (in this case count_results.txt) is readable via another shell session while the script continues to run. You can also pipe the output to concurrent jobs if required.
However, experienced DBAs might avoid row-counting all tables in this manner, and might choose instead to ensure that the runstats are always up-to-date for all tables, and accept recent estimates of row counts, which are visible in SYSCAT.TABLES.CARD once runstats are completed. If the stats are up to date, the CARD count is often good enough for many purposes. If exact counts are required, they are often valid only for a specific timestamp if the database is live.

DB2 Select Statement error after for loop in stored procedure

I've written a stored procedure which uses a for loop to execute a query for a list of views. It generates a dynamic sql statement for each view inside the for loop and then executes it, which inserts output into a declared temporary table.
The for loop works perfectly and it runs without errors, however if I add a select statement after the END FOR; to get the final output from the temporary table I get the error below. Does anyone have any ideas please?
Error 16/07/2018 10:43:41 0:00:00.007 DB2 Database Error: ERROR [42601] [IBM][DB2/AIX64] SQL0104N An unexpected token "select *" was found following "1; END FOR; ". Expected tokens may include: "<call>". LINE NUMBER=31. SQLSTATE=42601
SQL Code:
BEGIN
DECLARE SQLTEXT varchar(500);
DECLARE GLOBAL TEMPORARY TABLE SESSION.AS_USAGE_RESULTS(
temp table columns
);
FOR v as cur1 cursor for
select distinct viewname,viewschema
from syscat.VIEWS
DO
SET SQLTEXT = 'Dynamic Insert into temp table here'
PREPARE s1 FROM SQLTEXT;
EXECUTE s1;
END FOR;
select *
from SESSION.AS_USAGE_RESULTS;
DROP TABLE SESSION.AS_USAGE_RESULTS;
END
Your mistake is that if you wish to return a result-set from session.as_usage_results, then you must declare a cursor for its select, and open that cursor then end the sproc. This is a FAQ. There are examples in the IBM Db2 Server SAMPLES directory and in the Db2 Knowledge Center.
Inside the sproc, you can either use SELECT ... INTO, or use a select within a cursor, or use a SELECT as part of a SET statement.
You should not drop the session table in the procedure in case the result-set won't be consumed before the table gets dropped. Either drop the session table elsewhere or use an alternative design.
In your example you don't need cursor cur1, so below I show a stilted artificial example of what your might mean. It is artificial because you can see that the session table is also redundant for this example, but it shows the use of the cursor for the result-set.
--#SET TERMINATOR #
create or replace procedure dynproc1
language sql
specific dynproc1
dynamic result sets 1
BEGIN
DECLARE v_sqltext varchar(2000);
DECLARE c1 cursor with return to client for s1;
DECLARE GLOBAL TEMPORARY TABLE SESSION.AS_USAGE_RESULTS ( viewname varchar(128), viewschema varchar(128) );
insert into session.as_usage_results(viewname, viewschema) select viewname, viewschema from syscat.views;
set v_sqltext = 'select * from session.as_usage_results';
prepare s1 from v_sqltext;
open c1;
END
#

SQL Server - Pass image to stored procedure, invalid local var type

I have two tables that contain document content: one for temporary staging, other for permanent storage. The content is stored as type image (cannot change this since it's current functionality).
I need a stored procedure that does the following:
Pass in a TempDocumentID that exists in temp document table.
With that TempDocumentID, select image content from temp document table.
Exec existing stored procedure that takes an image parameter to insert into permanent document table.
My problem is two-fold:
I can't declare a local variable of type 'image' to fill from the select statement of temp table. It throws error 'The text, ntext, and image data types are invalid for local variables.'
I don't know of a way to exec stored proc with direct results from select statement of temp table.
Here is my SQL Fiddle example: http://sqlfiddle.com/#!3/09384/5
Thanks,
Greg
Try this, it doesn't get an error in SQL Fiddle. I believe it will pass the result from the sub-query:
CREATE PROCEDURE MoveDocumentFromTemp
(
#TempDocumentID numeric(18,0)
)
AS
BEGIN
EXEC InsertDocumentContentFinal (SELECT TempContent
FROM DocumentContentTemp (NOLOCK)
WHERE TempDocumentID = #TempDocumentID)
END
You should be able to use VARBINARY(MAX) with SQL Server 2005 and later.
CREATE PROCEDURE MoveDocumentFromTemp
(
#TempDocumentID numeric(18,0)
)
AS
BEGIN
DECLARE #ContentToMove varbinary(max)
SELECT #ContentToMove = cast(TempContent as varbinary(max))
FROM DocumentContentTemp (NOLOCK)
WHERE TempDocumentID = #TempDocumentID
EXEC InsertDocumentContentFinal #ContentToMove
END
GO
For SQL Server 2000, you'll just have to include the INSERT code from MoveDocumentFromTemp directly into your wrapper stored procedure.

How do I use Stored Procedure to view the entire contents of a table

How do I use a stored procedure to view the entire contents of a table in IBM DB2 (and anything else I guess, as SQL scrpting is the same throughout all of them)?
Thanks :)
In DB2, you must create a cursor that reads data from the table(s) you need and then open it to get the results. I'll give you a sample with no parameters.
CREATE PROCEDURE MY_SCHEMA.SP_SEL_TABLEX()
RESULT SET 1
LANGUAGE SQL
BEGIN
DECLARE C_TABLEX CURSOR FOR
SELECT COL1, COL2
FROM MY_SCHEMA.TABLEX;
OPEN C_TABLEX;
END;

SQL session/connection variables

I'm trying to find some equivalent to session variables in SQL. I want to be able to store and retrieve just a number but each connection to the database has a different number. It needs to persist from one batch to the next on the same connection.
I did have a solution that used a global cursor like this.
IF (SELECT CURSOR_STATUS('global','ChangeSet')) >= 0
BEGIN --Close and deallocate the cursor
Close ChangeSet
DEALLOCATE ChangeSet
END
--Create a new cursor
DECLARE ChangeSet CURSOR GLOBAL STATIC FOR
SELECT ChangeSet = #ChangeSet
--Open the cursor
OPEN ChangeSet
Each connection would have a different cursor so it worked, but this is not usable inside of a view. I guess if somebody can show me how to read this in a view that would be cool too.
I'm using MS SQL Server btw.
The CONTEXT_INFO property may be what you're looking for - it enables you to set and read a connection-specific binary value.
You could encode your numeric value to binary and store it in this property.
Starting from SQL 2016
EXEC sys.sp_set_session_context #key = N'language', #value = 'English';
SELECT SESSION_CONTEXT(N'language');
A temporary table survives a batch (including go). It's still connection specific:
create table #temp (val float)
insert #temp values (3.14)
go
select * from #temp