Firebird sql to insert a typical record from another table with only one different field - sql

I work on Firebird 2.5
and I have two tables, all their columns are similar except one has a primary key with auto increment and a not null foreign key field (A) for master table
I know I can use sql like this to insert all values from the two tables
insert into table1 select * from table2 where somthing = 'foo'
but what about the field (A) is there any way to insert this value manually in the same sql statement ? as this is the only field need to be entered manually
Thanks

You can specify both the source and target fields explicitly (and you should; don't use select * unless you have a specific reason to):
insert into table1
(
col1,
col2,
col3,
col4
)
select
col1,
col2,
col3,
'foo'
from table2
where something = 'foo'

Came upon this post because I was looking for a solution to do the same, but without hard-coding the field names because the fields maybe added/removed and didn't want to have to remember to update the copy record procedure.
After googling around for awhile, I came up with this solution:
select cast(list(trim(RDB$FIELD_NAME)) as varchar(10000))
from RDB$RELATION_FIELDS
where RDB$RELATION_NAME = 'YOUR_TABLE'
and RDB$FIELD_NAME not in ('ID') -- include other fields to NOT copy
into :FIELD_NAMES;
NEW_ID = next value for YOUR_TABLE_ID_GENERATOR;
execute statement '
insert into YOUR_TABLE (ID,' || FIELD_NAMES || ')
select ' || cast(:NEW_ID as varchar(20)) || ',' ||
FIELD_NAMES || '
from YOUR_TABLE
where ID = ' || cast(:ID_OF_RECORD_TO_COPY as varchar(20));
Hope this saves some time for anyone else who comes across this issue!

Related

SQL joining huge tables by excluding just one column in select statement [duplicate]

I'm trying to use a select statement to get all of the columns from a certain MySQL table except one. Is there a simple way to do this?
EDIT: There are 53 columns in this table (NOT MY DESIGN)
Actually there is a way, you need to have permissions of course for doing this ...
SET #sql = CONCAT('SELECT ', (SELECT REPLACE(GROUP_CONCAT(COLUMN_NAME), '<columns_to_omit>,', '') FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '<table>' AND TABLE_SCHEMA = '<database>'), ' FROM <table>');
PREPARE stmt1 FROM #sql;
EXECUTE stmt1;
Replacing <table>, <database> and <columns_to_omit>
(Do not try this on a big table, the result might be... surprising !)
TEMPORARY TABLE
DROP TABLE IF EXISTS temp_tb;
CREATE TEMPORARY TABLE ENGINE=MEMORY temp_tb SELECT * FROM orig_tb;
ALTER TABLE temp_tb DROP col_a, DROP col_f,DROP col_z; #// MySQL
SELECT * FROM temp_tb;
DROP syntax may vary for databases #Denis Rozhnev
Would a View work better in this case?
CREATE VIEW vwTable
as
SELECT
col1
, col2
, col3
, col..
, col53
FROM table
You can do:
SELECT column1, column2, column4 FROM table WHERE whatever
without getting column3, though perhaps you were looking for a more general solution?
If you are looking to exclude the value of a field, e.g. for security concerns / sensitive info, you can retrieve that column as null.
e.g.
SELECT *, NULL AS salary FROM users
To the best of my knowledge, there isn't. You can do something like:
SELECT col1, col2, col3, col4 FROM tbl
and manually choose the columns you want. However, if you want a lot of columns, then you might just want to do a:
SELECT * FROM tbl
and just ignore what you don't want.
In your particular case, I would suggest:
SELECT * FROM tbl
unless you only want a few columns. If you only want four columns, then:
SELECT col3, col6, col45, col 52 FROM tbl
would be fine, but if you want 50 columns, then any code that makes the query would become (too?) difficult to read.
While trying the solutions by #Mahomedalid and #Junaid I found a problem. So thought of sharing it. If the column name is having spaces or hyphens like check-in then the query will fail. The simple workaround is to use backtick around column names. The modified query is below
SET #SQL = CONCAT('SELECT ', (SELECT GROUP_CONCAT(CONCAT("`", COLUMN_NAME, "`")) FROM
INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'users' AND COLUMN_NAME NOT IN ('id')), ' FROM users');
PREPARE stmt1 FROM #SQL;
EXECUTE stmt1;
If the column that you didn't want to select had a massive amount of data in it, and you didn't want to include it due to speed issues and you select the other columns often, I would suggest that you create a new table with the one field that you don't usually select with a key to the original table and remove the field from the original table. Join the tables when that extra field is actually required.
You could use DESCRIBE my_table and use the results of that to generate the SELECT statement dynamically.
My main problem is the many columns I get when joining tables. While this is not the answer to your question (how to select all but certain columns from one table), I think it is worth mentioning that you can specify table. to get all columns from a particular table, instead of just specifying .
Here is an example of how this could be very useful:
select users.*, phone.meta_value as phone, zipcode.meta_value as zipcode
from users
left join user_meta as phone
on ( (users.user_id = phone.user_id) AND (phone.meta_key = 'phone') )
left join user_meta as zipcode
on ( (users.user_id = zipcode.user_id) AND (zipcode.meta_key = 'zipcode') )
The result is all the columns from the users table, and two additional columns which were joined from the meta table.
I liked the answer from #Mahomedalid besides this fact informed in comment from #Bill Karwin. The possible problem raised by #Jan Koritak is true I faced that but I have found a trick for that and just want to share it here for anyone facing the issue.
we can replace the REPLACE function with where clause in the sub-query of Prepared statement like this:
Using my table and column name
SET #SQL = CONCAT('SELECT ', (SELECT GROUP_CONCAT(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'users' AND COLUMN_NAME NOT IN ('id')), ' FROM users');
PREPARE stmt1 FROM #SQL;
EXECUTE stmt1;
So, this is going to exclude only the field id but not company_id
Yes, though it can be high I/O depending on the table here is a workaround I found for it.
SELECT *
INTO #temp
FROM table
ALTER TABLE #temp DROP COlUMN column_name
SELECT *
FROM #temp
It is good practice to specify the columns that you are querying even if you query all the columns.
So I would suggest you write the name of each column in the statement (excluding the one you don't want).
SELECT
col1
, col2
, col3
, col..
, col53
FROM table
I agree with the "simple" solution of listing all the columns, but this can be burdensome, and typos can cause lots of wasted time. I use a function "getTableColumns" to retrieve the names of my columns suitable for pasting into a query. Then all I need to do is to delete those I don't want.
CREATE FUNCTION `getTableColumns`(tablename varchar(100))
RETURNS varchar(5000) CHARSET latin1
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE res VARCHAR(5000) DEFAULT "";
DECLARE col VARCHAR(200);
DECLARE cur1 CURSOR FOR
select COLUMN_NAME from information_schema.columns
where TABLE_NAME=#table AND TABLE_SCHEMA="yourdatabase" ORDER BY ORDINAL_POSITION;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur1;
REPEAT
FETCH cur1 INTO col;
IF NOT done THEN
set res = CONCAT(res,IF(LENGTH(res)>0,",",""),col);
END IF;
UNTIL done END REPEAT;
CLOSE cur1;
RETURN res;
Your result returns a comma delimited string, for example...
col1,col2,col3,col4,...col53
I agree that it isn't sufficient to Select *, if that one you don't need, as mentioned elsewhere, is a BLOB, you don't want to have that overhead creep in.
I would create a view with the required data, then you can Select * in comfort --if the database software supports them. Else, put the huge data in another table.
At first I thought you could use regular expressions, but as I've been reading the MYSQL docs it seems you can't. If I were you I would use another language (such as PHP) to generate a list of columns you want to get, store it as a string and then use that to generate the SQL.
Based on #Mahomedalid answer, I have done some improvements to support "select all columns except some in mysql"
SET #database = 'database_name';
SET #tablename = 'table_name';
SET #cols2delete = 'col1,col2,col3';
SET #sql = CONCAT(
'SELECT ',
(
SELECT GROUP_CONCAT( IF(FIND_IN_SET(COLUMN_NAME, #cols2delete), NULL, COLUMN_NAME ) )
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #tablename AND TABLE_SCHEMA = #database
),
' FROM ',
#tablename);
SELECT #sql;
If you do have a lots of cols, use this sql to change group_concat_max_len
SET ##group_concat_max_len = 2048;
I agree with #Mahomedalid's answer, but I didn't want to do something like a prepared statement and I didn't want to type all the fields, so what I had was a silly solution.
Go to the table in phpmyadmin->sql->select, it dumps the query: copy, replace and done! :)
While I agree with Thomas' answer (+1 ;)), I'd like to add the caveat that I'll assume the column that you don't want contains hardly any data. If it contains enormous amounts of text, xml or binary blobs, then take the time to select each column individually. Your performance will suffer otherwise. Cheers!
Just do
SELECT * FROM table WHERE whatever
Then drop the column in you favourite programming language: php
while (($data = mysql_fetch_array($result, MYSQL_ASSOC)) !== FALSE) {
unset($data["id"]);
foreach ($data as $k => $v) {
echo"$v,";
}
}
The answer posted by Mahomedalid has a small problem:
Inside replace function code was replacing "<columns_to_delete>," by "", this replacement has a problem if the field to replace is the last one in the concat string due to the last one doesn't have the char comma "," and is not removed from the string.
My proposal:
SET #sql = CONCAT('SELECT ', (SELECT REPLACE(GROUP_CONCAT(COLUMN_NAME),
'<columns_to_delete>', '\'FIELD_REMOVED\'')
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = '<table>'
AND TABLE_SCHEMA = '<database>'), ' FROM <table>');
Replacing <table>, <database> and `
The column removed is replaced by the string "FIELD_REMOVED" in my case this works because I was trying to safe memory. (The field I was removing is a BLOB of around 1MB)
You can use SQL to generate SQL if you like and evaluate the SQL it produces. This is a general solution as it extracts the column names from the information schema. Here is an example from the Unix command line.
Substituting
MYSQL with your mysql command
TABLE with the table name
EXCLUDEDFIELD with excluded field name
echo $(echo 'select concat("select ", group_concat(column_name) , " from TABLE") from information_schema.columns where table_name="TABLE" and column_name != "EXCLUDEDFIELD" group by "t"' | MYSQL | tail -n 1) | MYSQL
You will really only need to extract the column names in this way only once to construct the column list excluded that column, and then just use the query you have constructed.
So something like:
column_list=$(echo 'select group_concat(column_name) from information_schema.columns where table_name="TABLE" and column_name != "EXCLUDEDFIELD" group by "t"' | MYSQL | tail -n 1)
Now you can reuse the $column_list string in queries you construct.
I wanted this too so I created a function instead.
public function getColsExcept($table,$remove){
$res =mysql_query("SHOW COLUMNS FROM $table");
while($arr = mysql_fetch_assoc($res)){
$cols[] = $arr['Field'];
}
if(is_array($remove)){
$newCols = array_diff($cols,$remove);
return "`".implode("`,`",$newCols)."`";
}else{
$length = count($cols);
for($i=0;$i<$length;$i++){
if($cols[$i] == $remove)
unset($cols[$i]);
}
return "`".implode("`,`",$cols)."`";
}
}
So how it works is that you enter the table, then a column you don't want or as in an array: array("id","name","whatevercolumn")
So in select you could use it like this:
mysql_query("SELECT ".$db->getColsExcept('table',array('id','bigtextcolumn'))." FROM table");
or
mysql_query("SELECT ".$db->getColsExcept('table','bigtextcolumn')." FROM table");
May be I have a solution to Jan Koritak's pointed out discrepancy
SELECT CONCAT('SELECT ',
( SELECT GROUP_CONCAT(t.col)
FROM
(
SELECT CASE
WHEN COLUMN_NAME = 'eid' THEN NULL
ELSE COLUMN_NAME
END AS col
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'employee' AND TABLE_SCHEMA = 'test'
) t
WHERE t.col IS NOT NULL) ,
' FROM employee' );
Table :
SELECT table_name,column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'employee' AND TABLE_SCHEMA = 'test'
================================
table_name column_name
employee eid
employee name_eid
employee sal
================================
Query Result:
'SELECT name_eid,sal FROM employee'
I use this work around although it may be "Off topic" - using mysql workbench and the query builder -
Open the columns view
Shift select all the columns you want in your query (in your case all but one which is what i do)
Right click and select send to SQL Editor-> name short.
Now you have the list and you can then copy paste the query to where ever.
If it's always the same one column, then you can create a view that doesn't have it in it.
Otherwise, no I don't think so.
I would like to add another point of view in order to solve this problem, specially if you have a small number of columns to remove.
You could use a DB tool like MySQL Workbench in order to generate the select statement for you, so you just have to manually remove those columns for the generated statement and copy it to your SQL script.
In MySQL Workbench the way to generate it is:
Right click on the table -> send to Sql Editor -> Select All Statement.
The accepted answer has several shortcomings.
It fails where the table or column names requires backticks
It fails if the column you want to omit is last in the list
It requires listing the table name twice (once for the select and another for the query text) which is redundant and unnecessary
It can potentially return column names in the wrong order
All of these issues can be overcome by simply including backticks in the SEPARATOR for your GROUP_CONCAT and using a WHERE condition instead of REPLACE(). For my purposes (and I imagine many others') I wanted the column names returned in the same order that they appear in the table itself. To achieve this, here we use an explicit ORDER BY clause inside of the GROUP_CONCAT() function:
SELECT CONCAT(
'SELECT `',
GROUP_CONCAT(COLUMN_NAME ORDER BY `ORDINAL_POSITION` SEPARATOR '`,`'),
'` FROM `',
`TABLE_SCHEMA`,
'`.`',
TABLE_NAME,
'`;'
)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE `TABLE_SCHEMA` = 'my_database'
AND `TABLE_NAME` = 'my_table'
AND `COLUMN_NAME` != 'column_to_omit';
I have a suggestion but not a solution.
If some of your columns have a larger data sets then you should try with following
SELECT *, LEFT(col1, 0) AS col1, LEFT(col2, 0) as col2 FROM table
If you use MySQL Workbench you can right-click your table and click Send to sql editor and then Select All Statement This will create an statement where all fields are listed, like this:
SELECT `purchase_history`.`id`,
`purchase_history`.`user_id`,
`purchase_history`.`deleted_at`
FROM `fs_normal_run_2`.`purchase_history`;
SELECT * FROM fs_normal_run_2.purchase_history;
Now you can just remove those that you dont want.

selecting empty string is SELECT is slow

Working on speeding up a query and I've noticed for some reason the more empty columns added to a query the slower it gets.
With only the Id column the query returns 100k records in approx. 1 second.
If I add about 20 empty columns it goes to 4 seconds.
Questions
- What is the default data type of the string in SQL?
- Any way to speed this up?
SELECT Id,
'' as col1,
'' as col2,
'' as col3
FROM myTable
It will depend on how many rows are in your myTable. For ex: If you have 905k rows on mytable, Basically SQL is creating 20 Columns with ' ' for 905k rows
I just tried it in my own table that has 805k rows. For every increment of Columns I add, SQL creates '' values for each row.
Hope this helps you understand it clearer.
The default data type seems to be a varchar(1) -- you can insert it into a temp table and check the temp table structure to confirm. One option you can try is declare a variable and use it rather than the empty spaces:
declare #space varchar(40) = ''
SELECT
id,
#space as col1,
#space as col2,
#space as col3
FROM dbo.[table]

SQL - conditionally set column values to NULL

I have a table - some_table which has a number of columns and some of them have some invalid value in some rows which need to transformed into NULL.
I cannot use the below due as mutating the original table is not allowed by permissions for one and also it needs to be repeated for all column names.
UPDATE some_table TABLE## SET column_name = NULL WHERE column_name = 'invalid value';
So it needs to be a 'SELECT' operation to create a new table with invalid values converted to NULL - is there a quick way to do this ?
Updating with an answer from #Jonny below
NULLIF is a good option. However is there a way to apply it to all columns rather having to do it for each column separately - sometimes the number of columns is pretty huge.
You could use a NULLIF
Have a look at 9.16.3. NULLIF
https://www.postgresql.org/docs/current/static/functions-conditional.html
SELECT NULLIF('invalid value', column_name)
FROM some_table
How about something like:
INSERT INTO some_table2 (column_name, ...) SELECT * FROM some_table WHERE column_name <> 'invalid value';
INSERT INTO some_table2 (column_name, ...) SELECT null, ... FROM some_table WHERE column_name = 'invalid_value';

How to replace underscore with a blank space with a regular expression in SQL

I'm trying to insert post codes into my database but getting rid of the underscores.
I have a table called FeedDataSetMapping that is used to map the fields before they get inserted:
INSERT INTO FeedDataSetMapping (
[source_field]
,[database_field]
,[template_id]
,[conversion_id]
,[order_id]
,[values_group]
,[direct_value]
,[value_regex]
,[condition_regex]
,[split_separator]
,[enclosing_character]
,[cumulative_field]
,[cumulative_format])
VALUES
('manufacturerId','manufacturer_Id',#template_id,0,0,null,null,null,null,null,null,null,null),
('dealership','leasing_broker_name',#template_id,0,0,null,null,null,null,null,null,null,null),
('manufacturersDealerId','supplier_ref',#template_id,0,0,null,null,19,null,null,null,null,null),
('address1','address1',#template_id,0,0,null,null,null,null,null,null,null,null),
('address2','address2',#template_id,0,0,null,null,null,null,null,null,null,null),
('postcode','post_code',#template_id,0,0,null,null,null,null,null,null,null,null),
('telephone','telephone',#template_id,0,0,null,null,null,null,null,null,null,null),
('fax','fax_number',#template_id,0,0,null,null,null,null,null,null,null,null),
('email','email',#template_id,0,0,null,null,null,null,null,null,null,null),
('website','web_address',#template_id,0,0,null,null,null,null,null,null,null,null),
('NewCarSales','service_mask',#template_id,0,0,null,1,null,'^(?!(?i:^0$|^n$|^no$|^f$|^false$|^$))',null,null,1,null),
('UsedCarSales','service_mask',#template_id,0,0,null,2,null,'^(?!(?i:^0$|^n$|^no$|^f$|^false$|^$))',null,null,1,null),
('Servicing','service_mask',#template_id,0,0,null,8,null,'^(?!(?i:^0$|^n$|^no$|^f$|^false$|^$))',null,null,1,null),
('Repairs','service_mask',#template_id,0,0,null,16,null,'^(?!(?i:^0$|^n$|^no$|^f$|^false$|^$))',null,null,1,null),
('Longitude','longitude',#template_id,0,0,null,null,null,null,null,null,null,null),
('Latitude','latitude',#template_id,0,0,null,null,null,null,null,null,null,null)
This already contains some condition regex that in case that this field contains some text it converts it to true or false respectively.
What I need is a condition_regex that gets rid of these underscores and replaces it with a blank space i.e: 'GDB_A45' to 'GDB A45'. I don't know much about regex so any idea would be greatly appreciated. Thanks in advance!
SQL Server does not have much of regular expression support, but in this case I don't think you need it. You can do a simple replace:
UPDATE mytable
SET mycolumn = REPLACE(mycolumn, '_', ' ')
WHERE mycolumn LIKE '%[_]%'
To do this while updating you can use INSERT ... SELECT instead of INSERT ... VALUES:
INSERT INTO mytable (mycolumn)
SELECT REPLACE('my data 1', '_', ' ') UNION
SELECT REPLACE('my data 2', '_', ' ') UNION
SELECT REPLACE('my_data_3', '_', ' ') UNION
...
There will be some maximum number of unions you can do, so you should split your inserts into batches with this method.
Or, you could define a trigger on the target table that will do the job for you:
CREATE TRIGGER mytrigger ON mytable
AFTER INSERT AS
BEGIN
UPDATE mytable
SET mytable.mycolumn = REPLACE(i.mycolumn, '_', ' ')
FROM mytable
INNER JOIN inserted i
ON i.id = mytable.id
AND i.mycolumn LIKE '%[_]%'
END
... where it is assumed your table has a primary key named id.
Well after been thinking a while I got to the conclusion that would be easier if I replace the underscore from the scraped data during the scraping (in the c# code) before I generate the XML file. That would avoid me a lot of headaches. Anyway thank you for your help guys ;)

How can I copy a record, changing only the id?

My table has a large number of columns. I have a command to copy some data - think of it as cloning a product - but as the columns may change in the future, I would like to only select everything from the table and only change the value of one column without having to refer to the rest.
Eg instead of:
INSERT INTO MYTABLE (
SELECT NEW_ID, COLUMN_1, COLUMN_2, COLUMN_3, etc
FROM MYTABLE)
I would like something resembling
INSERT INTO MYTABLE (
SELECT * {update this, set ID = NEW_ID}
FROM MYTABLE)
Is there a simple way to do this?
This is a DB2 database on an iSeries, but answers for any platform are welcome.
You could do this:
create table mytable_copy as select * from mytable;
update mytable_copy set id=new_id;
insert into mytable select * from mytable_copy;
drop table mytable_copy;
I don't think this is doable entirely within SQL without going to the trouble of creating a temp table. Doing it in memory should be much faster. Beware if you go the temporary table route that you must choose a unique name for your table for each function invocation to avoid the race condition where your code runs twice at the same time and mangles two rows of data into one temp table.
I don't know what kind of language you're using but it should be possible to obtain a list of fields in your program. I would do it like this:
array_of_field_names = conn->get_field__list;
array_of_row_values = conn->execute ("SELECT... ");
array_of_row_values ["ID"] = new_id_value
insert_query_string = "construct insert query string from list of field names and values";
conn->execute (insert_query_string);
Then you can encapsulate that as a function and just call it specifying table, old id and new id and it'd work it's magic.
In Perl code the following snippet would do:
$table_name = "MYTABLE";
$field_name = "ID";
$existing_field_value = "100";
$new_field_value = "101";
my $q = $dbh->prepare ("SELECT * FROM $table_name WHERE $field_name=?");
$q->execute ($existing_field_value);
my $rowdata = $q->fetchrow_hashref; # includes field names
$rowdata->{$field_name} = $new_field_value;
my $insq = $dbh->prepare ("INSERT INTO $table_name (" . join (", ", keys %$rowdata) .
") VALUES (" . join (", ", map { "?" } keys %$rowdata) . ");";
$insq->execute (values %$rowdata);
Hope this helps.
Ok, try this:
declare #othercols nvarchar(max);
declare #qry nvarchar(max);
select #othercols = (
select ', ' + quotename(name)
from sys.columns
where object_id = object_id('tableA')
and name <> 'Field3'
and is_identity = 0
for xml path(''));
select #qry = 'insert mynewtable (changingcol' + #othercols + ') select newval' + #othercols;
exec sp_executesql #qry;
Before you run the "sp_executesql" line, please do "select #qry" to see what the command is that you're going to run.
And of course, you may want to stick this in a stored procedure and pass in a variable instead of the 'Field3' bit.
Rob
Your example should almost work.
Just add the column names of the new table to it.
INSERT INTO MYTABLE
(id, col1, col2)
SELECT new_id,col1, col2
FROM TABLE2
WHERE ...;
i've never worked with db2 but in mssql you could solve it with following procedure. this solution only works if you dont care what new id the items get.
1.) create new table with same scheme but where the id column incrementes automatically. (mssql "identitity specification = 1, identity increment = 1)
2.) than a simple
insert into newTable(col1, col2, col3)
select (col1, col2, col3) from oldatable
should be enough, be sure not to include your id colum in the above statement