the situation is this:
I have 200 txt files with different names like 601776.txt each file's name is actually an ID_foo and it contains some data like this (2 columns):
04004 Albánchez
04006 Albox
04008 Alcóntar
04009 Alcudia de Monteagud
.
.
.
now I wanna BULK INSERT these TXT files into a SQL Server Table which has 3 column one of these columns should be the name of the txt file. I'm using a PHP script, so I made a loop to get the file names and then what?
BULK INSERT Employee_Table
FROM '../home/601776.txt'
WITH (
FIELDTERMINATOR ='\t',
ROWTERMINATOR = ''\n''
)
how can i set the third column while bulk inserting with $file_name variable in each loop?
Do you think it is a better idea if it is possible to insert the table by reading the txt file line by line? And how?
Thanks
This is one of the few times that a cursor is actually ideal in SQL Server. Here's a way. Once you see the PRINT statement and are satisfied you can comment it out and uncomment out the two lines below it. I put some logic in to add the file name and a processed date which is usually needed, but your table definition would need these columns. It should get the idea across.
---------------------------------------------------------------------------------------------------------------
--Set some variables
---------------------------------------------------------------------------------------------------------------
DECLARE #dt VARCHAR(10) --date variable but stored as VARCHAR for formatting of file name
DECLARE #fileLocation VARCHAR(128) = 'E:\DATA_TRANSFERS\' --production location which is \\issqlstd01 but the xp_dirtree didn't like this
DECLARE #sql NVARCHAR(4000) --dynamic sql variable
DECLARE #fileName VARCHAR(128) --full file name variable
---------------------------------------------------------------------------------------------------------------
--Get a list of all the file names in the directory
---------------------------------------------------------------------------------------------------------------
IF OBJECT_ID('tempdb..#FileNames') IS NOT NULL DROP TABLE #FileNames
CREATE TABLE #FileNames (
id int IDENTITY(1,1)
,subdirectory nvarchar(512)
,depth int
,isfile bit)
INSERT #FileNames (subdirectory,depth,isfile)
EXEC xp_dirtree #fileLocation, 1, 1
---------------------------------------------------------------------------------------------------------------
--Create a cursor to fetch the file names
---------------------------------------------------------------------------------------------------------------
DECLARE c CURSOR FOR
select subdirectory from #FileNames where isfile = 1
OPEN c
FETCH NEXT FROM c INTO #fileName
---------------------------------------------------------------------------------------------------------------
--For each file, bulk insert
---------------------------------------------------------------------------------------------------------------
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = 'BULK INSERT Employee_Table FROM '''+ #fileLocation + #fileName +''' WITH (FIELDTERMINATOR = ''\t'',KEEPNULLS,ROWTERMINATOR = ''0x0a'')'
--Try the bulk insert, if error is thrown log the error
--Also update the Table Columns which aren't a part of the original file (load date and original file name)
BEGIN TRY
PRINT(#sql)
--EXEC(#sql)
--UPDATE Employee_Table SET OrigFile = #fileName, LoadDate = GETDATE() WHERE OrigFile IS NULL
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE()
END CATCH
FETCH NEXT FROM c INTO #fileName
END
CLOSE c
DEALLOCATE c
GO
Related
I'm trying to write a PHP program to update a MySQL table entry according to a phone number. The phone numbers in the database are entered without limitations and are typically formatted in the XXX-XXX-XXXX way, but sometimes have other characters due to typos. In order to ensure the query works every time, I want to remove all non-numeric characters from the entries so that I can compare the entries to phone numbers formatted like XXXXXXXXXX coming from a separate source.
I've done some research and found some solutions but am unsure how to incorporate them into the PHP script. I am fairly new to MySQL and most of the solutions provided user defined MySQL functions and I don't know how to put them into the PHP script and use them with the query I already have.
Here's one of the solutions I found:
CREATE FUNCTION [dbo].[CleanPhoneNumber] (#Temp VARCHAR(1000))
RETURNS VARCHAR(1000) AS BEGIN
DECLARE #KeepValues AS VARCHAR(50)
SET #KeepValues = '%[^0-9]%'
WHILE PATINDEX(#KeepValues, #Temp) > 0
SET #Temp = STUFF(#Temp, PATINDEX(#KeepValues, #Temp), 1, '')
RETURN #Temp
END
And this is the query I need the solution for:
$sql = "SELECT pid AS pid FROM patient_data " .
"WHERE pid = '$pID' AND phone_cell = '$phone_number';";
The query should return the data in the pid column for a single patient, so if the phone number is 1234567890 and the pid is 15, 15 should be returned. I have no output at the moment.
The example function definition is Transact-SQL (i.e. for Microsoft SQL Server), it's not valid MySQL syntax.
A function like this doesn't go "into" the PHP code. The function gets created on the MySQL database as a separate step, similar to creating a table. The PHP code can call (reference) the defined function just like it references builtin functions such as DATE_FORMAT or SUBSTR.
The SELECT statement follows the pattern of SQL that is vulnerable to SQL Injection. Any potentially unsafe values that are incorporated into SQL text must be properly escaped. A better pattern is to use prepared statements with bind placeholders.
As an example of a MySQL function:
DELIMITER $$
CREATE FUNCTION clean_phone_number(as_phone_string VARCHAR(1024))
RETURNS VARCHAR(1024)
DETERMINISTIC
BEGIN
DECLARE c CHAR(1) DEFAULT NULL;
DECLARE i INT DEFAULT 0;
DECLARE n INT DEFAULT 0;
DECLARE ls_digits VARCHAR(20) DEFAULT '0,1,2,3,4,5,6,7,8,9';
DECLARE ls_retval VARCHAR(1024) DEFAULT '';
IF ( as_phone_string IS NULL OR as_phone_string = '' ) THEN
RETURN as_phone_string;
END IF;
SET n := CHAR_LENGTH(as_phone_string);
WHILE ( i < n ) DO
SET i := i + 1;
SET c := SUBSTR(as_phone_string,i,1);
IF ( FIND_IN_SET(c,ls_digits) ) THEN
SET ls_retval := CONCAT(ls_retval,c);
END IF;
END WHILE;
RETURN ls_retval;
END$$
DELIMITER ;
We can execute these statements in the mysql command line client, connected as a user with sufficient privilege, to create the function.
This isn't necessarily the best way to write the function, but it does serve as a demonstration.
Once the function is created, we can reference it a SQL statement, for example:
SELECT t.foo
, clean_phone_number(t.foo)
FROM ( SELECT '1' AS foo
UNION ALL SELECT '1-888-TAXICAB'
UNION ALL SELECT '888-555-1212'
UNION ALL SELECT '+=_-()*&^%$##"''<>?/;:"abc...xyz'
UNION ALL SELECT ''
UNION ALL SELECT NULL
) t
I am trying to create a stored procedure using PHP. My reading indicates the best way to do this by running the .sql file using the 'exec' command in PHP.
In testing i created a file named amtv3_create_routines.sql with this contents:
DELIMITER //
DROP PROCEDURE IF EXISTS createTc //
CREATE PROCEDURE createTc()
BEGIN
drop table if exists v3_tc;
CREATE TABLE v3_tc (
source BIGINT UNSIGNED NOT NULL ,
dest BIGINT UNSIGNED NOT NULL ,
PRIMARY KEY (source, dest) )
ENGINE = InnoDB;
insert into v3_tc (source, dest)
select distinct rel.sourceid, rel.destinationid
from rf2_ss_relationships rel inner join rf2_ss_concepts con
on rel.sourceid = con.id and con.active = 1
where rel.typeid = (select distinct conceptid from rf2_ss_descriptions where term = 'is a')
and rel.active = 1;
REPEAT
insert into v3_tc (source, dest)
select distinct b.source, a.dest
from v3_tc a
join v3_tc b on a.source = b.dest
left join v3_tc c on c.source = b.source and c.dest = a.dest
where c.source is null;
set #x = row_count();
select concat('Inserted ', #x);
UNTIL #x = 0 END REPEAT;
create index idx_v3_tc_source on v3_tc (source);
create index idx_v3_tc_dest on v3_tc (dest);
END //
DELIMITER;
This code works fine when I manually enter it into mysql 5.6.22
However if I save the file and from the prompt enter the command.
mysql -uroot -p -hlocalhost amtv3 < [full path]/amtv3_create_routines.sql
I have tried saving the file using utf8 encoding and windows 1252 encoding.
From the command prompt, there is no feedback, and the procedure is not created.
In PHP I am using the codeigniter framework. If I use the db->query method I can create the stored procedure, however the database loses connection. issuing $db->reconnect() works, but not reliably.
Any suggestions on how to create the stored procedure?
Omitting the space in the last line, DELIMITER; should result in a syntax error (there may be some other reason why this error is not being printed).
DELIMITER is only a feature of certain MySQL clients, and not a feature of the server. Therefore, when executing a .sql file directly, the server will interpret the first semi-colon as the end of the first statement and DELIMITER // will be seen as a syntax violation:
Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DELIMITER //
CREATE PROCEDURE . . .
Discovered this here:
https://github.com/go-sql-driver/mysql/issues/351#issuecomment-120081608
The Solution?
Simply don't change the delimiter. The BEGIN and END already delimit a compound statement.
Source:
https://www.tutorialspoint.com/mysql/mysql_begin_end_compound.htm
You can try this
mysql -h localhost -U <username> -d <database_name> -f /home/user/<procedure_name>.sql
Trying to create my first stored procedure, I have researched extensively and could not find a way to input the code via phpMyAdmin so downloaded MYSQL Workbench 6.0 and put my script in that way- but it throws a 2014 error (Commands out of sync, you cannot run this command now). However I notice that Workbench seems to create two lines - the first says running and the second the error.
The routine is to update three tables and insert into another when a contract is issued. Contracts are made up of bundles and they in turn consist of items which relate to specific problems in another table.
Is it my code or should I include some clearing command at the end and if so what should that be? The procedure input is a contract id, user id and their IP address (for logging).
In PHP when, eventually, using a CALL how would I clear results, as I keep seeing Stackoverflow 2014 answers where second calls fail? Also how can I get this onto an ubuntu server either via phpMyAdmin or if with putty where should I ftp the sql script to?
DELIMITER //
DROP PROCEDURE IF EXISTS ContrctAwardStatusLog;
CREATE PROCEDURE ContrctAwardStatusLog( IN c_Id INT(8), IN u_Id INT(11), IN u_Ip varchar(20) )
BEGIN
Block1: BEGIN
DECLARE done INT DEFAULT 0;
DECLARE b_id INT DEFAULT 0;
DECLARE citm_id INT DEFAULT 0;
DECLARE r_id INT DEFAULT 0;
DECLARE c_1 CURSOR FOR SELECT bundle_id FROM bundles WHERE contract_id = c_Id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN c_1;
REPEAT
FETCH c_1 INTO b_id ;
Block2: BEGIN
DECLARE done2 INT DEFAULT 0;
DECLARE c_2 CURSOR FOR SELECT contitem_id, issue_id FROM c_items WHERE bundle_id= b_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done2 = 1;
OPEN c_2;
REPEAT
FETCH c_2 INTO citm_id r_id;
INSERT INTO track_status (rowid, WStatusBy, WStatus, WStatusWhen) VALUES(r_id, u_Id, 13,NOW());
UPDATE h_issues SET WStatus='13' WHERE RowID = r_id;
UPDATE c_items SET act_state='13' WHERE contitem_id=citm_id;
UNTIL done2
END REPEAT;
CLOSE c_2;
END Block2;
UPDATE bundles SET bundle_stat = '13' WHERE bundle_id = b_id;
UNTIL done
END REPEAT;
CLOSE c_1;
END Block1;
END //
DELIMITER ;
I finally sorted this myself.
I needed to put the DROP ..if exists BEFORE the DELIMITER Setting and there was a syntax error in the FETCH after OPEN c_2. It was missing a comma separator.
DROP PROCEDURE IF EXISTS ContrctAwardStatusLog;
DELIMITER //
CREATE PROCEDURE ContrctAwardStatusLog( IN c_Id INT(8), IN u_Id INT(11), IN u_Ip varchar(20) )
BEGIN
Block1: BEGIN
DECLARE done INT DEFAULT 0;
DECLARE b_id INT DEFAULT 0;
DECLARE citm_id INT DEFAULT 0;
DECLARE r_id INT DEFAULT 0;
DECLARE c_1 CURSOR FOR SELECT bundle_id FROM bundles WHERE contract_id = c_Id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN c_1;
REPEAT
FETCH c_1 INTO b_id ;
Block2: BEGIN
DECLARE done2 INT DEFAULT 0;
DECLARE c_2 CURSOR FOR SELECT contitem_id, issue_id FROM c_items WHERE bundle_id= b_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done2 = 1;
OPEN c_2;
REPEAT
FETCH c_2 INTO citm_id, r_id;
INSERT INTO track_status (rowid, WStatusBy, WStatus, WStatusWhen) VALUES(r_id, u_Id, 13,NOW());
UPDATE h_issues SET WStatus='13' WHERE RowID = r_id;
UPDATE c_items SET act_state='13' WHERE contitem_id=citm_id;
UNTIL done2
END REPEAT;
CLOSE c_2;
END Block2;
UPDATE bundles SET bundle_stat = '13' WHERE bundle_id = b_id;
UNTIL done
END REPEAT;
CLOSE c_1;
END Block1;
END //
DELIMITER ;
I have to update the column File on the TABLE TEST. This column contains the files related to the row. Each file is separated by a |.
An example could be
ID NAME FILE
1 apple fruit.png | lemon.png
Now when I add a new file to the FILE column I use this query:
$link->query("UPDATE TEST SET File = CONCAT(File, '$dbfilename') WHERE id = '$p_id'")
where $dbfilename can be e.g. pineapple.jpg |
The problem is that, if $dbfilename is already on the File values, it will be added another time, resulting double.
How can I check if File contains already $dbfilename, and if yes, don't add id, or even don't execute the query?
This is not a good way of storing information in a database. But I'll get to that in a second. To directly answer your question, you could use this as your SQL query:
UPDATE TEST SET File = CONCAT(File, '$dbfilename')
WHERE id='$p_id'
AND File NOT LIKE '%$dbfilename%'
AND Lingua='$linguadilavoro'
However, this may cause some issues when one file pineapple.jpg and you try to add another-pineapple.jpg
Really, I think you should consider how this is a horribly bad approach to databases. Consider breaking the files off into a second table. For example:
# a table for the fruit names
CREATE TABLE fruits (
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(250) NOT NULL,
UNIQUE INDEX(name)
);
# a table for file names
CREATE TABLE files (
fileid INT UNSIGNED NOT NULL DEFAULT PRIMARY KEY AUTO_INCREMENT,
fruitid INT UNSIGNED,
filename VARCHAR(250),
UNIQUE INDEX(fruitid, filename)
);
# find all of the fruits with their associated files
SELECT fruits.id, fruits.name, files.filename
FROM fruits LEFT JOIN files ON fruits.id=files.fruitid
# add a file to a fruit
INSERT INTO files (fruitid, filename)
VALUES ('$fruitID', '$filename')
ON DUPLICATE KEY UPDATE fruitid=LAST_INSERT_ID(id)
You will have to select out the FILE for the id.
then use explode to break it into an array
then check use in_array to determine if it should be added or not
Here is some (untested) code for guidance
$stmt = $link->query("SELECT File File from TEST WHERE id = '$p_id'");
$rec = $stmt->fetchAssoc();
$files = explode(" | ",$rec["FILE"]);
if (!in_array($dbfilename, $files)){
// add to FILE
} else {
// it's already there
}
I would redesign your table structure and add a new table File with the following columns instead of using a varchar field for multiple values:
Table Test
TableId, Name
Table File
FileId, TestId, FileName
I am in a bit of a pickle. I have a stored procedure that accepts an argument and returns the results of a query. The query is using an IN statement.
Here's the structure of the SP:
CREATE OR REPLACE
PROCEDURE EXAMPLE_SP
(
arg VARCHAR2,
argRS1 OUT cursors.rs
)
AS
l_test VARCHAR2(255) := arg;
BEGIN
OPEN argRS1 FOR
SELECT * FROM TABLE1
WHERE LOCATION IN (l_test);
END EXAMPLE_SP;
The number of values within the IN statement can be variable. The options for IN are coming from selected form checkboxes on the UI side.
I am using PHP to retrieve the selected checkbox values. I have already tried imploding the values into a comma deliminated string.
My logic for that was that the query would then look like this:
l_test = 'val1, val2, val3';
SELECT * FROM TABLE1
WHERE LOCATION IN (val1, val2, val3);
But that didn't work. I am not sure how to proceed. Thanks in advance for any constructive comments or suggestions.
You can add this comma separated input parameter as a varchar() and use following where statement:
where (','||l_test||',' like '%,'||LOCATION||',%')
for example if l_test='2,3,4,5' and LOCATION=3 we get:
where (',2,3,4,5,' like '%,3,%')
and it's TRUE if LOCATION value is in this list.
I think the location that you have selected is VARCHAR,so for that you need to convert the list as shown below
l_test = '''val1''||','|| ''val2''||','||''val3''';
So that your final query look like
SELECT * FROM TABLE1
WHERE LOCATION IN ('val1', 'val2', 'val3');
You can do like this also
CREATE OR REPLACE
PROCEDURE EXAMPLE_SP
(
arg VARCHAR2,
argRS1 OUT cursors.rs
)
AS
l_test VARCHAR2(255) := arg;
BEGIN
l_test:=''''||replace(l_test,',',''',''')||'''';
OPEN argRS1 FOR
SELECT * FROM TABLE1
WHERE LOCATION IN (l_test);
END EXAMPLE_SP;
Note:I have not tested this ,but i think this way you will achieve what you want
I would do this without using string manipulation. Theoretically there may currently be little risk of SQL Injection because you're using checkboxes it's best to implement good practice at the beginning so if anything changes you don't put yourself at risk.
The second benefit is that you are still able to utilise any indexes on your column, which you wouldn't be able to do if you use like '%....
To do this you can utilise a table function and an external object to populate your "in" list.
As an example I'm going to return the OBJECT_NAME from USER_OBJECTS.
If I create two tables:
create table tmp_test ( a number );
create table tmp_test2 ( a number );
and an object to hold the list of tables, or in your case locations.
create type t_test_object is table of varchar2(30);
Next, here's the equivalent of your procedure. It's a function that returns a SYS_REFCURSOR. It accepts the T_TEST_OBJECT as a parameter, which means this first needs to be populated before being passed to the function.
create or replace function select_many (
Ptest_object in t_test_object
) return sys_refcursor is
l_curs sys_refcursor;
begin
open l_curs for
select object_name
from user_objects
where object_name in ( select *
from table(Ptest_object)
);
return l_curs;
end;
Lastly, here's an example of how to use this set-up. Notice how an instance of T_TEST_OBJECT gets populated with multiple values. This object then gets passed to the function to return your cursor. Lastly, to display the values I loop through the cursor. Obviously you may want to utilise the cursor and populate the TYPE differently.
SQL> declare
2
3 l_nested_table t_test_object := new t_test_object();
4 l_cursor sys_refcursor;
5 -- Used for display demonstration only.
6 l_object_name user_objects.object_name%type;
7
8 begin
9
10 l_nested_table.extend;
11 l_nested_table(l_nested_table.last) := 'TMP_TEST';
12 l_nested_table.extend;
13 l_nested_table(l_nested_table.last) := 'TMP_TEST2';
14
15 l_cursor := select_many(l_nested_table);
16
17 loop -- Display example, not really relevant
18 fetch l_cursor into l_object_name;
19 exit when l_cursor%notfound;
20 dbms_output.put_line(l_object_name);
21 end loop;
22
23 end;
24 /
TMP_TEST
TMP_TEST2
PL/SQL procedure successfully completed.
You can use Oracle examples from Oracle Documentation: http://docs.oracle.com/cd/B28359_01/win.111/b28378/basfeat.htm#sthref207
Look here - return a table:
Can an SQL procedure return a table?
And here's another example:
PACKAGE emp_actions IS
TYPE EnameTabTyp IS TABLE OF emp.ename%TYPE INDEX BY BINARY_INTEGER;
TYPE SalTabTyp IS TABLE OF emp.sal%TYPE INDEX BY BINARY_INTEGER;
...
PROCEDURE hire_batch (ename_tab IN EnameTabTyp, sal_tab IN SalTabTyp, ...);
PROCEDURE log_names (ename_tab IN EnameTabTyp);
END emp_actions;