Reputation: 63
I am trying to make some sense of a xBase type database with some 2000 tables. Rather than importing them all into a SQL Server database, I wanted to import the tables one-by-one using a 'SELECT INTO tmpDBF' statement, then extract what I want to know like table structure and value ranges for each of the columns. Then, when I import the next table I want to be able to run the same query against a differently structured tmpDBF table.
I was hoping to do this using a cross apply, but I come up against the above error message.
select cols.column_name 'Name', cols.data_type 'Type', mv.minV 'Minimum'
from information_schema.columns cols
cross apply (select MIN(cols.column_name) minV FROM tmpDBF ) mv
where cols.table_name = 'tmpDBF'
Is there way to restructure the query or did I turn into a dead-end street?
Added on October 6:
Given tmpDBF
Who | Zip
--------|------
Charlie | 97689
Foxtrot | 92143
Delta | 12011
I would like to see the following result
Name | Type | Minimum | Maximum
-----|---------|---------|--------
who | varchar | Charlie | Foxtrot
Zip | int | 12011 | 96789
I realise that the Minimum and Maximum columns need to be cast as varchars.
Upvotes: 2
Views: 6173
Reputation: 11823
This is not possible for two reasons.
But to get you something similar to what you are looking for you can flip the problem around like this:
MS SQL Server 2008 Schema Setup:
CREATE TABLE dbo.a(c1 INT, c2 INT, c3 DATE);
INSERT INTO dbo.a VALUES(1,2,'2013-04-05'),(4,5,'2010-11-10'),(7,8,'2012-07-09');
Query 1:
SELECT
MIN(c1) c1_min,MAX(c1) c1_max,
MIN(c2) c2_min,MAX(c2) c2_max,
MIN(c3) c3_min,MAX(c3) c3_max
FROM dbo.a;
| C1_MIN | C1_MAX | C2_MIN | C2_MAX | C3_MIN | C3_MAX |
|--------|--------|--------|--------|------------|------------|
| 1 | 7 | 2 | 8 | 2010-11-10 | 2013-04-05 |
That gives you all the column minima and maxima in a single row. (It's not dynamic yet. Stay with me...)
To make it a little more readable you can use a sort of UNPIVOT like this:
Query 2:
SELECT
CASE X.FN WHEN 1 THEN 'MIN' ELSE 'MAX' END AS FN,
CASE X.FN WHEN 1 THEN c1_min ELSE c1_max END AS c1,
CASE X.FN WHEN 1 THEN c2_min ELSE c2_max END AS c2,
CASE X.FN WHEN 1 THEN c3_min ELSE c3_max END AS c3
FROM(
SELECT
MIN(c1) c1_min,MAX(c1) c1_max,
MIN(c2) c2_min,MAX(c2) c2_max,
MIN(c3) c3_min,MAX(c3) c3_max
FROM dbo.a)AGG
CROSS JOIN (VALUES(1),(2))X(FN)
ORDER BY X.FN;
| FN | C1 | C2 | C3 |
|-----|----|----|------------|
| MIN | 1 | 2 | 2010-11-10 |
| MAX | 7 | 8 | 2013-04-05 |
Now to make it dynamic we have to build that query on the fly, like this:
Query 3:
DECLARE @cmd NVARCHAR(MAX);
SET @cmd =
'SELECT CASE X.FN WHEN 1 THEN ''MIN'' ELSE ''MAX'' END AS FN'+
(SELECT ',CASE X.FN WHEN 1 THEN '+name+'_min ELSE '+name+'_max END AS '+name
FROM sys.columns WHERE object_id = OBJECT_ID('dbo.a')
FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)')+
' FROM(SELECT '+
STUFF((SELECT ',MIN('+name+') '+name+'_min,MAX('+name+') '+name+'_max'
FROM sys.columns WHERE object_id = OBJECT_ID('dbo.a')
FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'),1,1,'')+
' FROM dbo.a)AGG CROSS JOIN (VALUES(1),(2))X(FN) ORDER BY X.FN;';
EXEC(@cmd);
| FN | C1 | C2 | C3 |
|-----|----|----|------------|
| MIN | 1 | 2 | 2010-11-10 |
| MAX | 7 | 8 | 2013-04-05 |
This query takes the columns of the table at runtime, builds the appropriate query dynamically and executes it. It contains the table name ('dbo.a') in three places. If you want it to work with different tables you need to replace all three.
Upvotes: 2
Reputation: 69594
Try something like
select cols.column_name 'Name', cols.data_type 'Type', mv.minV 'Minimum'
from information_schema.columns cols
cross apply (select MIN(cols.column_name) minV FROM tmpDBF
WHERE tmpDBF.CommonCol = cols.CommonCol) mv
where cols.table_name = 'tmpDBF'
Upvotes: 0