PostgreSQL "DESCRIBE TABLE" - database

How do you perform the equivalent of Oracle's DESCRIBE TABLE in PostgreSQL with psql command?

Try this (in the psql command-line tool):
\d+ tablename
See the manual for more info.

In addition to the PostgreSQL way (\d 'something' or \dt 'table' or \ds 'sequence' and so on)
The SQL standard way, as shown here:
select column_name, data_type, character_maximum_length, column_default, is_nullable
from INFORMATION_SCHEMA.COLUMNS where table_name = '<name of table>';
It's supported by many db engines.

If you want to obtain it from query instead of psql, you can query the catalog schema. Here's a complex query that does that:
SELECT
f.attnum AS number,
f.attname AS name,
f.attnum,
f.attnotnull AS notnull,
pg_catalog.format_type(f.atttypid,f.atttypmod) AS type,
CASE
WHEN p.contype = 'p' THEN 't'
ELSE 'f'
END AS primarykey,
CASE
WHEN p.contype = 'u' THEN 't'
ELSE 'f'
END AS uniquekey,
CASE
WHEN p.contype = 'f' THEN g.relname
END AS foreignkey,
CASE
WHEN p.contype = 'f' THEN p.confkey
END AS foreignkey_fieldnum,
CASE
WHEN p.contype = 'f' THEN g.relname
END AS foreignkey,
CASE
WHEN p.contype = 'f' THEN p.conkey
END AS foreignkey_connnum,
CASE
WHEN f.atthasdef = 't' THEN d.adsrc
END AS default
FROM pg_attribute f
JOIN pg_class c ON c.oid = f.attrelid
JOIN pg_type t ON t.oid = f.atttypid
LEFT JOIN pg_attrdef d ON d.adrelid = c.oid AND d.adnum = f.attnum
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
LEFT JOIN pg_constraint p ON p.conrelid = c.oid AND f.attnum = ANY (p.conkey)
LEFT JOIN pg_class AS g ON p.confrelid = g.oid
WHERE c.relkind = 'r'::char
AND n.nspname = '%s' -- Replace with Schema name
AND c.relname = '%s' -- Replace with table name
AND f.attnum > 0 ORDER BY number
;
It's pretty complex but it does show you the power and flexibility of the PostgreSQL system catalog and should get you on your way to pg_catalog mastery ;-). Be sure to change out the %s's in the query. The first is Schema and the second is the table name.

You can do that with a psql slash command:
\d myTable describe table
It also works for other objects:
\d myView describe view
\d myIndex describe index
\d mySequence describe sequence
Source: faqs.org

The psql equivalent of DESCRIBE TABLE is \d table.
See the psql portion of the PostgreSQL manual for more details.

This should be the solution:
SELECT * FROM information_schema.columns
WHERE table_schema = 'your_schema'
AND table_name = 'your_table'

You may do a \d *search pattern * with asterisks to find tables that match the search pattern you're interested in.

In addition to the command line \d+ <table_name> you already found, you could also use the information-schema to look up the column data, using info_schema.columns
SELECT *
FROM info_schema.columns
WHERE table_schema = 'your_schema'
AND table_name = 'your_table'

Use the following SQL statement
SELECT DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'tbl_name'
AND COLUMN_NAME = 'col_name'
If you replace tbl_name and col_name, it displays data type of the particular coloumn that you looking for.

You can use this :
SELECT attname
FROM pg_attribute,pg_class
WHERE attrelid=pg_class.oid
AND relname='TableName'
AND attstattarget <>0;

In MySQL , DESCRIBE table_name
In PostgreSQL , \d table_name
Or , you can use this long command:
SELECT
a.attname AS Field,
t.typname || '(' || a.atttypmod || ')' AS Type,
CASE WHEN a.attnotnull = 't' THEN 'YES' ELSE 'NO' END AS Null,
CASE WHEN r.contype = 'p' THEN 'PRI' ELSE '' END AS Key,
(SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid), '\'(.*)\'')
FROM
pg_catalog.pg_attrdef d
WHERE
d.adrelid = a.attrelid
AND d.adnum = a.attnum
AND a.atthasdef) AS Default,
'' as Extras
FROM
pg_class c
JOIN pg_attribute a ON a.attrelid = c.oid
JOIN pg_type t ON a.atttypid = t.oid
LEFT JOIN pg_catalog.pg_constraint r ON c.oid = r.conrelid
AND r.conname = a.attname
WHERE
c.relname = 'tablename'
AND a.attnum > 0
ORDER BY a.attnum

This variation of the query (as explained in other answers) worked for me.
SELECT
COLUMN_NAME
FROM
information_schema.COLUMNS
WHERE
TABLE_NAME = 'city';
It's described here in details:
http://www.postgresqltutorial.com/postgresql-describe-table/

To improve on the other answer's SQL query (which is great!), here is a revised query. It also includes constraint names, inheritance information, and a data types broken into it's constituent parts (type, length, precision, scale). It also filters out columns that have been dropped (which still exist in the database).
SELECT
n.nspname as schema,
c.relname as table,
f.attname as column,
f.attnum as column_id,
f.attnotnull as not_null,
f.attislocal not_inherited,
f.attinhcount inheritance_count,
pg_catalog.format_type(f.atttypid,f.atttypmod) AS data_type_full,
t.typname AS data_type_name,
CASE
WHEN f.atttypmod >= 0 AND t.typname <> 'numeric'THEN (f.atttypmod - 4) --first 4 bytes are for storing actual length of data
END AS data_type_length,
CASE
WHEN t.typname = 'numeric' THEN (((f.atttypmod - 4) >> 16) & 65535)
END AS numeric_precision,
CASE
WHEN t.typname = 'numeric' THEN ((f.atttypmod - 4)& 65535 )
END AS numeric_scale,
CASE
WHEN p.contype = 'p' THEN 't'
ELSE 'f'
END AS is_primary_key,
CASE
WHEN p.contype = 'p' THEN p.conname
END AS primary_key_name,
CASE
WHEN p.contype = 'u' THEN 't'
ELSE 'f'
END AS is_unique_key,
CASE
WHEN p.contype = 'u' THEN p.conname
END AS unique_key_name,
CASE
WHEN p.contype = 'f' THEN 't'
ELSE 'f'
END AS is_foreign_key,
CASE
WHEN p.contype = 'f' THEN p.conname
END AS foreignkey_name,
CASE
WHEN p.contype = 'f' THEN p.confkey
END AS foreign_key_columnid,
CASE
WHEN p.contype = 'f' THEN g.relname
END AS foreign_key_table,
CASE
WHEN p.contype = 'f' THEN p.conkey
END AS foreign_key_local_column_id,
CASE
WHEN f.atthasdef = 't' THEN d.adsrc
END AS default_value
FROM pg_attribute f
JOIN pg_class c ON c.oid = f.attrelid
JOIN pg_type t ON t.oid = f.atttypid
LEFT JOIN pg_attrdef d ON d.adrelid = c.oid AND d.adnum = f.attnum
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
LEFT JOIN pg_constraint p ON p.conrelid = c.oid AND f.attnum = ANY (p.conkey)
LEFT JOIN pg_class AS g ON p.confrelid = g.oid
WHERE c.relkind = 'r'::char
AND f.attisdropped = false
AND n.nspname = '%s' -- Replace with Schema name
AND c.relname = '%s' -- Replace with table name
AND f.attnum > 0
ORDER BY f.attnum
;

You can also check using below query
Select * from schema_name.table_name limit 0;
Expmple : My table has 2 columns name and pwd. Giving screenshot below.
*Using PG admin3

In postgres \d is used to describe the table structure.
e.g. \d schema_name.table_name
this command will provide you the basic info of table such as, columns, type and modifiers.
If you want more info about table use
\d+ schema_name.table_name
this will give you extra info such as, storage, stats target and description

The best way to describe a table such as a column, type, modifiers of columns, etc.
\d+ tablename or \d tablename

When your table is not part of the default schema, you should write:
\d+ schema_name.table_name
Otherwise, you would get the error saying that "the relation doesn not exist."

When your table name starts with a capital letter you should put your table name in the quotation.
Example: \d "Users"

Use this command
\d table name
like
\d queuerecords
Table "public.queuerecords"
Column | Type | Modifiers
-----------+-----------------------------+-----------
id | uuid | not null
endtime | timestamp without time zone |
payload | text |
queueid | text |
starttime | timestamp without time zone |
status | text |

1) PostgreSQL DESCRIBE TABLE using psql
In psql command line tool, \d table_name or \d+ table_name to find the information on columns of a table
2) PostgreSQL DESCRIBE TABLE using information_schema
SELECT statement to query the column_names,datatype,character maximum length of the columns table in the information_schema database;
SELECT
COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH
from INFORMATION_SCHEMA.COLUMNS where table_name = 'tablename';
For more information https://www.postgresqltutorial.com/postgresql-describe-table/

I'll add the pg_dump command even thou the psql command was requested. because it generate an output more common to previous MySQl users.
# sudo -u postgres pg_dump --table=my_table_name --schema-only mydb

/dt is the commad which lists you all the tables present in a database. using
/d command and /d+ we can get the details of a table. The sysntax will be like
* /d table_name (or) \d+ table_name

The command below can describe multiple tables simply
\dt <table> <table>
The command below can describe multiple tables in detail:
\d <table> <table>
The command below can describe multiple tables in more detail:
\d+ <table> <table>

I worked out the following script for get table schema.
'CREATE TABLE ' || 'yourschema.yourtable' || E'\n(\n' ||
array_to_string(
array_agg(
' ' || column_expr
)
, E',\n'
) || E'\n);\n'
from
(
SELECT ' ' || column_name || ' ' || data_type ||
coalesce('(' || character_maximum_length || ')', '') ||
case when is_nullable = 'YES' then ' NULL' else ' NOT NULL' end as column_expr
FROM information_schema.columns
WHERE table_schema || '.' || table_name = 'yourschema.yourtable'
ORDER BY ordinal_position
) column_list;

Related

Create postgres view from table with dynamic casting

I want to create a generic query that will allow me to create a view (from a table) and convert all Array columns into strings.
Something like:
CREATE OR REPLACE VIEW view_1 AS
SELECT *
for each column_name in columns
CASE WHEN pg_typeof(column_name) == TEXT[] THEN array_to_string(column_name)
ELSE column_name
FROM table_1;
I guess that I can do that with stored procedure but I'm looking for solution in pure SQL, if it can be to much complex.
Here is a query to do such conversion. You can then customize it to create the view and execute it.
SELECT
'CREATE OR REPLACE VIEW my_table_view AS SELECT ' || string_agg(
CASE
WHEN pg_catalog.format_type(pg_attribute.atttypid, pg_attribute.atttypmod) LIKE '%[]' THEN 'array_to_string(' || pg_attribute.attname || ', '','') AS ' || pg_attribute.attname
ELSE pg_attribute.attname
END, ', ' ORDER BY attnum ASC)
|| ' FROM ' || min(pg_class.relname) || ';'
FROM
pg_catalog.pg_attribute
INNER JOIN
pg_catalog.pg_class ON pg_class.oid = pg_attribute.attrelid
INNER JOIN
pg_catalog.pg_namespace ON pg_namespace.oid = pg_class.relnamespace
WHERE
pg_attribute.attnum > 0
AND NOT pg_attribute.attisdropped
AND pg_namespace.nspname = 'my_schema'
AND pg_class.relname = 'my_table'
; \gexec
Example:
create table tarr (id integer, t_arr1 text[], regtext text, t_arr2 text[], int_arr integer[]);
==>
SELECT id, array_to_string(t_arr1) AS t_arr1, regtext, array_to_string(t_arr2) AS t_arr2, int_arr FROM tarr;

Make variable's data type match column's data type

A local variable's data type needs to match the data type of an existing table column.
In the past, I would look up the column's data type and manually match, like so:
-- schema follows...
CREATE TABLE [dbo].[TestTable]
(
[Id] BIGINT NOT NULL PRIMARY KEY,
[valueholder] NVARCHAR(MAX) NULL
)
...
-- manually set data type to match above
DECLARE #tempvalueholder AS NVARCHAR(MAX)
The trouble is, if the schema changes somewhere along the line, I'd have to manually look up and update.
Assuming the column and table names remain constant, is there some way to tie a local variable's data type to a column's data type?
I know how to get the data type in a way similar to this, but can't figure out how to hook up to a variable declaration:
SELECT DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME='testtable'
AND COLUMN_NAME='valueholder'
You need to create "DECLARE #VariableName DATATYPE" dynamically. This means that the variable and the rest of your code should be in the scope of the dynamic SQL. If this fits your needs, you can try this:
DECLARE
#DataType1 VARCHAR(16)
, #DataType2 VARCHAR(16)
, #DynamicSQL NVARCHAR(MAX) = N'';
SELECT
#DataType1 = UPPER(DATA_TYPE)
, #DataType2 =
CASE
WHEN (DATA_TYPE IN ('char', 'nchar')) THEN CONCAT(UPPER(DATA_TYPE), '(', CHARACTER_MAXIMUM_LENGTH, ')')
WHEN (DATA_TYPE IN ('varchar', 'nvarchar')) THEN CASE WHEN (CHARACTER_MAXIMUM_LENGTH = -1) THEN CONCAT(UPPER(DATA_TYPE), '(MAX)') ELSE CONCAT(UPPER(DATA_TYPE), '(', CHARACTER_MAXIMUM_LENGTH, ')') END
WHEN (DATA_TYPE IN ('decimal', 'numeric')) THEN CONCAT(UPPER(DATA_TYPE), '(', NUMERIC_PRECISION, ', ', NUMERIC_SCALE, ')')
WHEN (DATA_TYPE = 'float') THEN CASE WHEN (NUMERIC_PRECISION = 53) THEN UPPER(DATA_TYPE) ELSE CONCAT(UPPER(DATA_TYPE), '(', NUMERIC_PRECISION, ')') END
WHEN (DATA_TYPE = 'real') THEN CASE WHEN (NUMERIC_PRECISION = 24) THEN UPPER(DATA_TYPE) ELSE CONCAT(UPPER(DATA_TYPE), '(', NUMERIC_PRECISION, ')') END
WHEN (DATA_TYPE = 'image') THEN NULL
WHEN (DATA_TYPE = 'time') THEN CASE WHEN (DATETIME_PRECISION = 7) THEN UPPER(DATA_TYPE) ELSE CONCAT(UPPER(DATA_TYPE), '(', DATETIME_PRECISION, ')') END
WHEN (DATA_TYPE = 'varbinary') THEN CASE WHEN (CHARACTER_MAXIMUM_LENGTH = -1) THEN CONCAT(UPPER(DATA_TYPE), '(MAX)') ELSE CONCAT(UPPER(DATA_TYPE), '(', CHARACTER_MAXIMUM_LENGTH, ')') END
ELSE UPPER(DATA_TYPE)
END
FROM INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_SCHEMA = 'SchemaName'
AND TABLE_NAME = 'TableName'
AND COLUMN_NAME = 'ColumnName';
IF(#DataType2 IS NULL)
BEGIN RAISERROR (N'Data type %s is invalid for local variables.', 16, 1, #DataType1); END
ELSE
BEGIN
SET #DynamicSQL += N'
DECLARE #VariableName ' + #DataType2 + ';'
SET #DynamicSQL += N'
SET #VariableName = 15;
SELECT #VariableName AS [#VariableName];
';
EXEC (#DynamicSQL);
END
unlike Oracle this is not supported in sql-server.
The only thing you can do is when the schema of a table changes, find all procedures, triggers, functions that access this table and check/correct the declaration.
Here is a small query that helps you find all procedures, functions and triggers where that table is used :
SELECT DISTINCT
o.name AS Object_Name,
o.type_desc,
m.*
FROM sys.sql_modules m
INNER JOIN sys.objects o ON m.object_id = o.object_id
WHERE m.definition Like '%MyTableName%';
I can suggest using a temp table which will have exactly the same structure of the originating table.
So even table columns' data types are changed, your stored procedure codes will not be affected.
But it has also coding overload for developers
Here is a sample
create procedure TestTable10_readbyId (#Id bigint)
as
begin
DECLARE #tempvalueholder AS NVARCHAR(MAX)
select * into #t from TestTable10 where 1 = 0
insert into #t (Id) values (#Id)
update #t
set valueholder = TestTable10.valueholder
from #t
inner join TestTable10 on TestTable10.Id = #t.Id
and TestTable10.Id = #Id
select #tempvalueholder = valueholder
from TestTable10
where Id = #Id
select #tempvalueholder as valueholder
select valueholder from #t
end
You see I have maintained both methods
One is using a data variable declaration
Other is creating a temp table, which generates logically a variable for each column in the select list
Of course we need a row with NULL values for the second solution.
I actually don't prefer this method because you have to think all those during coding. So I have inserted a dummy row with null values but only the PK field that I believe it will be useful in following code blocks
Instead of returning a variable by setting its value first, I update the temp table column and then return this row column in this example.

SQL Query, column SHOULD be present, but results states, it is not

I am having a hard time grasping why this query is telling me the TaxPayerID is NOT found, when in the beginning, I am clearly checking for it and only using the databases, which should contain the TaxPayerID column in the nTrucks table.
sp_MSforeachdb
'
IF EXISTS (SELECT * FROM [?].INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''nTrucks'' AND COLUMN_NAME = ''TaxPayerID'')
BEGIN
SELECT "?", nTrucks.UnitNumber, ntrucks.Companyid, nCompanyData.CompanyName, nTrucks.Owner, nTrucks.TaxPayerID
FROM nTrucks
INNER JOIN nCompanyData ON nTrucks.CompanyID = nCompanyData.CompanyID
WHERE nTrucks.Owner like ''%Trucker%''
END
'
I am getting multiple 'Invalid column name 'TaxPayerID'.' errors, I assume it is from the databases NOT containing this column.
If anyone here can throw me a bone, a simple "you're a dummy, do it this way!", I would be very appreciative.
JF
You're a dummy! (you asked for it) :)
How to debug this error:
Locate the database that throws an error and try executing an actual SQL query on it directly to see if it will compile:
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''nTrucks'' AND COLUMN_NAME = ''TaxPayerID'')
BEGIN
SELECT nTrucks.UnitNumber, ntrucks.Companyid, nCompanyData.CompanyName, nTrucks.Owner, nTrucks.TaxPayerID
FROM nTrucks
INNER JOIN nCompanyData ON nTrucks.CompanyID = nCompanyData.CompanyID
WHERE nTrucks.Owner like ''%Trucker%''
END
It will fail.
Now you know that SQL server checks schema at query parse time rather than run time.
Then you follow #GordonLinoff suggestion and convert the SELECT query into dynamic SQL as follows:
sp_MSforeachdb
'
IF EXISTS (SELECT * FROM [?].INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''nTrucks'' AND COLUMN_NAME = ''TaxPayerID'')
BEGIN
EXEC(
''SELECT "?", nTrucks.UnitNumber, ntrucks.Companyid, nCompanyData.CompanyName, nTrucks.Owner, nTrucks.TaxPayerID
FROM [?]..nTrucks
INNER JOIN [?]..nCompanyData ON nTrucks.CompanyID = nCompanyData.CompanyID
WHERE nTrucks.Owner like ''''%Trucker%''''
'' )
END
'
(I hope I got my quotes right)
If your query is supposed to reference a central nCompareData table then remove [?].. before nCompareData

PostgreSQL query fails over PGNP provider for SQL Server

This query is failing in SQL Server over the PGNP provider:
SELECT 1 AS x
FROM pg_catalog.pg_attribute a
LEFT OUTER JOIN pg_catalog.pg_index i
ON (i.indrelid = a.attrelid
AND a.attnum = i.indkey[a.attnum-1])
This is the error message returned:
The OLE DB provider "PGNP" for linked server "XXX" indicates that either the object has no columns or the current user does not have permissions on that object.
If I run this query directly on the PostgreSQL server with pgAdmin, it returns results. Furthermore, if I remove the clause "a.attnum = i.indkey[a.attnum-1]", the query works over the PGNP provider. (I need this clause in my original query to identify which of a table's columns is the primary key.)
What is the issue, and how do I resolve it?
For the record, this is the complete query that I am executing, from which the query above was excerpted:
SELECT c.relname as table_name, a.attname as column_name
, t.typname as type_name
, i.indisunique AS is_unique_key
, i.indisprimary AS is_primary_key
, CASE t.typname
WHEN 'bpchar' THEN 'char('+CAST(atttypmod - 4 AS VARCHAR)+')'
WHEN 'numeric' THEN 'numeric('+CAST((atttypmod - 4)/65536 AS VARCHAR)
+','+CAST((atttypmod - 4)%65536 AS VARCHAR)
+')'
WHEN 'text' THEN 'varchar(max)'
WHEN 'varchar' THEN 'varchar('+CASE atttypmod WHEN -1 THEN 'max' ELSE CAST(atttypmod-4 AS VARCHAR) END+')'
ELSE t.typname
END as type_name_1
, CAST(CASE atttypmod WHEN -1 THEN NULL ELSE atttypmod - 4 END AS INT4) AS type_precision_scale
FROM pg_catalog.pg_attribute a
JOIN pg_catalog.pg_class c on ( a.attrelid = c.oid)
JOIN pg_catalog.pg_namespace n on (c.relnamespace = n.oid)
JOIN pg_catalog.pg_type t on (a.atttypid = t.oid)
LEFT OUTER JOIN pg_catalog.pg_index i
ON (c.oid = i.indrelid
AND i.indrelid = a.attrelid
AND a.attnum = i.indkey[a.attnum-1])
WHERE c.relkind = 'r' and a.attnum > 0
AND n.nspname = 'public'

SQL Server SP, Function, View source line counter

Is there any utility availble to count the total lines of user created Stored Procedure, Function, Views in a Database?
For SQL Server 2005 and 2008.
This includes all code including blank lines and trailing blank lines, but not the last line (no CRLF). So it's averages out... but it would always be an approximation anyway.
WITH CRLF AS
(
SELECT
CHARINDEX('
', definition) AS CRLF,
SM.[object_ID]
FROM
sys.sql_modules SM
WHERE
OBJECT_NAME([object_ID]) not in ('fn_diagramobjects', 'sp_alterdiagram', 'sp_creatediagram', 'sp_dropdiagram', 'sp_helpdiagramdefinition', 'sp_helpdiagrams', 'sp_renamediagram', 'sp_upgraddiagrams', 'sysdiagrams')
UNION ALL
SELECT
CHARINDEX('
', definition, C.CRLF + 2),
SM.[object_ID]
FROM
sys.sql_modules SM
JOIN
CRLF C ON SM.[object_ID] = C.[object_ID]
WHERE
CHARINDEX('
', definition, C.CRLF + 2) > C.CRLF
)
SELECT
COUNT(*)
FROM
CRLF
OPTION
(MAXRECURSION 0)
Edit:
You may need OBJECTPROPERTY(SM.[object_ID], 'IsMSShipped') = 0 or explicitly exclusions for diagram code etc
Edit 2:
From other solution in otehr answer, corrected to not give "-1" for check constraints and apply same filters/types
select t.sp_name, sum(t.lines_of_code) as lines_ofcode, t.type_desc
from
(
select o.name as sp_name,
(len(c.text) - len(replace(c.text, char(13), ''))) as lines_of_code,
case when o.xtype = 'P' then 'Stored Procedure'
when o.xtype in ('FN', 'IF', 'TF') then 'Function'
end as type_desc
from sysobjects o
inner join syscomments c
on c.id = o.id
where --o.xtype in ('V', 'P', 'FN', 'IF', 'TF', 'TR')
--and
o.category = 0
AND
o.name not in ('fn_diagramobjects', 'sp_alterdiagram', 'sp_creatediagram', 'sp_dropdiagram', 'sp_helpdiagramdefinition', 'sp_helpdiagrams', 'sp_renamediagram', 'sp_upgraddiagrams', 'sysdiagrams')
) t
group by t.sp_name, t.type_desc
order by 1
COMPUTE SUM (sum(t.lines_of_code))
They all give the same results here on several databases. eg 4607 for a SQL Server 2005 SP2 ReportServer database...
Not that I know of, but you could look through the stuff in sysobjects and execute sp_helptext on each proc and view and count the newlines.
If you want a non CTE based solution you could do something like this:
select sum(newlines) from
(
select newlines = (datalength(definition) - datalength(replace(definition, '
', ' '))) / 2 from sys.sql_modules
) as a

Resources