One of the main problem of sybase (and other database) is the way it handle disk space. You must create « disk devices », which can be RAW or file on a filesystem. Then give some space of it to a database, and perhaps create or extend segments.

Of course, this may sound a good way to deal with it. Then comes the time when you need some storage for something else, or another database.

You start realizing that the 15 Gb you added to your database, thinking your data will grow as you reach your 1.000.000.000 subscriber… but your stalled at 150 :)

So, using some sybase tools, you realize only 5% of the space is used. Then you wonder « how will I get the free space for something else ? »

The Sybase and Sybase support is simple : you can’t. But…

Of course you can. It’s only data after all. Your data.
The solution is simpler to say than to do : backup data and schema, drop the database, drop the devices, re-create the devices, the database then import.

One would say : eaaassssyyyyyy.
Then try to do it without using dump/load.

The fact is the dump/load Sybase command uses internal Sybase page structures. If you have a 15Gb database, even 5% filled, your dump will have a 15Gb size, minus the compression you set for the dump.
When loading, each page will be put in a page of the new database device. Meaning, you need to have a new database the same size as the old one. The load tool will tell you that if you are wrong.
And this is even worse than you may think : you need to have the same amount of data and log space (or more) !!

So… bcp out, then bcp in.

BCP is a command line tool provided by sybase. It load/unload data from/to a flat file.

First, get the schema out of the database.
For this I use the dbschema perl tool. Get it at http://www.midsomer.org
This is what I do :

mkdir DB1_schema
cd DB1_schema
/opt/monitor/perl/bin/perl   /home/prune/dbschema/dbschema.pl -SDB1 -Usa -Ppassword -O1 -m -A -V -b --target-db my_new_db

You will get a directory per database inside DB1. In each directory you will have a file per type of object : table, stored procs, key, foreign key, view…

The use of « –target-db my_new_db » will create DBP in/out scripts with the right name to import data to a different database (my_new_db instead of my_old_db). See under for the BCP stuffs anyway.

Then, get the data out using bcp.
You need to bcp out the data each table at a time.
The database I’m using have almost a hundred tables. One solution is to use an option of the dbschema to create a bcp shell script. Or you can use the sp__bcp stored procedure from the awesom package you can find at http://www.edbarlow.com
sp__bcp need options so it can create the bcp command line :

  • DSQUERY : the dataserver name
  • database_name : the database to dump from
  • sa : the login of the admin user or a user who can select every tables
  • password : …
  • out : in or out, depending of the script you want to create

When creating the bcp in script, be sure to be in the right database you just have bcp out BUT put the database name where you will load the data, if different.

use my_old_db
go
sp__bcp "DB1", "my_old_db", "sa", "password", "out"
go

Also get the in script :

use my_old_db
go
sp__bcp "DB1", "my_new_db", "sa", "password", "in", "dat", "-b 10000"
go

This proc will not create any script. You need to copy the output to a file, make it executable.
The last parameter will split the load in chuck of 10000 lines, which will help to not fill the logs. Maybe 50000 or 100000 would be better. please comment.
Then you can create back your database on the right devices, with the right size, etc.

disk init
name      = 'NewdbData',
physname  = '/opt/db/dbdata/DB1_NewdbData',
vdevno    = 7,
size      = 2560000,
cntrltype = 0,
dsync = 'false'
go

disk init
name      = 'NewdbLog',
physname  = '/opt/db/dbdata/DB1_NewdbLog',
vdevno    = 8,
size      = 153600,
cntrltype = 0,
dsync = 'false'
go

CREATE DATABASE my_new_db ON NewdbData = 5000 LOG ON PhileasLog = 300
use master
go
sp_dboption phileas, 'single user', true
go
exec sp_logdevice phileas, PhileasLog
go
sp_dboption phileas, 'single user', false
go

You are ready to re-create the structure of the database. The « table structure » as Sybase support call it.

Go to the dbschema directory, in the db you want to create, and load the scripts one at a time, in the right order. Note that, if your developpers do fancy things in theire stored procs, the schema export from dbschema may not work. I had to use an extract from « CAST », then use dos2unix to translate some french accents to unix code.
Here is how I do it then :

dos2unix procs-cast.sql procs.sql

isql -Usa -w512 -SDB1 -Ppassword < defaults.sql > defaults.sql.err
isql -Usa -w512 -SDB1 -Ppassword < groups.sql > groups.sql.err
isql -Usa -w512 -SDB1 -Ppassword < types.sql > types.sql.err
isql -Usa -w512 -SDB1 -Ppassword < rules.sql   > rules.sql.err
isql -Usa -w512 -SDB1 -Ppassword < tables.sql > tables.sql.err
isql -Usa -w512 -SDB1 -Ppassword < keys.sql > keys.sql.err
isql -Usa -w512 -SDB1 -Ppassword < views.sql > views.sql.err
isql -Usa -w512 -SDB1 -Ppassword < indexes.sql > indexes.sql.err
isql -Usa -w512 -SDB1 -Ppassword < fkeys.sql > fkeys.sql.err
isql -Usa -w512 -SDB1 -Ppassword < trigs.sql > trigs.sql.err
isql -Usa -w512 -SDB1 -Ppassword < procs.sql > procs.sql.err

Once this is done, just check you have all your stuffs :

select type, count(*) from sysobjects  group by type
go
 type
 ---- -----------
 D             16 types
 P           1072 procs
 R             15 rule
 RI           357 index
 S             26 system tables
 TR           538 trigger
 U            555 table
 V             94 view

You should have the same result as on your old database. In fact, I don’t :)
But the dev told me that it was normal. Some procs are « wrong » but they are never used on a history database, which is what I’m working on right now.

You can then load the data, using the bcp script.
If your files are big, you may fill the logs. You need to set a procedure to dump them. This proc will automaticaly be run by a trigger. You can add it in your database, or in sybsystemprocs. This proc is made for logging the dump, and dumping to a file. I just changed it to dump with « no_log », as we don’t care of the log during import.

Here is the proc :

CREATE PROC sp_thresholdaction   (
  @dbname varchar(30),
  @segmentname varchar(30),
  @space_left int,
  @status int )
AS
BEGIN
declare @msg  varchar(256),
        @date_actuelle  datetime,
        @nom_fichier  varchar(256),
        @date_fichier  varchar(20),
        @rep_sauve_log  varchar(40)

select @rep_sauve_log = 'compress::6::/DUMP_SYBASE/DB1/DumpDATA/'

select @date_actuelle = getdate()

select @date_fichier =
	convert (char(8), @date_actuelle, 112) +'-'+
	convert(varchar(2),datepart(hh,@date_actuelle)) +
	convert(varchar(2),datepart(mi,@date_actuelle)) +
	convert(varchar(2),datepart(ss,@date_actuelle))

select @nom_fichier = @rep_sauve_log + @dbname + @date_fichier + '.trn',
       @msg = @date_fichier + ' Warning: dump transaction for ' + @dbname + ':' + @segmentname  + ' (' + convert(varchar(12),@space_left) + ' free) on ' + @nom_fichier

print @msg

if @segmentname = 'logsegment'
     dump tran @dbname with no_log
return
END

go

Finaly, run you bcp in script, and you are all set. I hope… :)
Don’t forget to change the DB owner and options, but if you are that far I’m sure you can do it.