An SQL returns results directly in tables which you can check in all kinds of ways. You can create any number of temporary tables, with the same schema as your business tables, and check all kinds of invariants.
There's absolutely no reason why unit testing SQL should be harder than anything else, considering a single query as the "unit" of testing.
In fact, that's to the built-in checks, constraints and types a RDBMS has, you are freed from having to unit test all kinds of crap too (similar to having less to unit test in Haskell vs Ruby).
You need to prepare test data, you need to update and maintain the test data. That is already a big barrier to entry.
The actual testing involves three simple steps: setting the initial state of the database, run your queries/procs, verify the results. This will be unbearably slow even for a small test set. So you start to make things complicated by trying to be smart, like only revert the state you modified, or using SQLite for tests and Postgres for production, or by running the database server on a RAM filesystem, etc, etc.
I've seen a few people go down the rabbit hole and noone came up with a solution I could be happy with.
Debugging mm possibly slightly harder in that you might have to have a 3rd monitor for Toad or work Manager - but you code your sprocs properly in the first place you should not have that many problems that jump between code and sql .
Just saying its hard doesn't help in that case we ought to still be coding in GWBASIC
Have your statements (including those that create stored procedures on setup, migrations, etc) on text files, and just load those into your Git or whatever.
You're right about non-SProc code; just deploy all of it. Do the same thing with SProc code!
What's tough about keeping all your code in files that start with "CREATE OR REPLACE FUNCTION <funcname>", and just firing them all at the DB (within a transaction, if you like)?
I don't actively advocate putting all the code in sprocs, but I can see advantages. I also don't advocate using PHP, and yet people demonstrably build some great websites with it.
A migration was a way to roll a database forwards or backwards; there were tools to create simple ones, and one was able to write whatever Python & SQL one wished in order to handle more complex cases. One might even archive off a column somewhere when deleting it, and load it up back when restoring it, if one wished.
Since the migrations were all just source code, they were perfectly well-suited to source control.
It was a really powerful system; I'm surprised that it hasn't seen wider acceptance.