SQL Server 2008 + Creating a cross-tab stored procedure - sql

I have two tables that I want to join and create a crosstab table in SQL 2008:
TableA:
Auto_ID | Fiscal_Period | Amount
1 | 01012012 | NULL
1 | 01022012 | 80
1 | 01032012 | NULL
2 | 01012012 | NULL
2 | 01022012 | 10
TABLEB:
Auto_ID | Row_ID | StaticData
1 | 1 | sampledata
2 | 2 | data1
I would like to use cross table to dynamic create the following table structure:
Row_ID | StaticData | FiscalPeriod(01012012) | FiscalPeriod(01022012) | FiscalPeriod(01032012)
1 | sampledata | NULL | 80 | NULL
2 | data1 | NULL | 10 | NULL
My current query joins the tables correctly; however, I am having difficulty transposing the fiscal periods into my header row.
SELECT *
FROM (SELECT
B.Row_Id as RowID, B.StaticData as StaticData, A.Fiscal_Period AS FPPD
FROM TableA A
LEFT JOIN TableB B ON A.Auto_ID = B.Auto_ID)

This is what I would do:
First create some test data:
CREATE TABLE tblA (Auto_ID INT,Fiscal_Period VARCHAR(100),Amount FLOAT)
CREATE TABLE tblB (Auto_ID INT,Row_ID INT,StaticData VARCHAR(100))
INSERT INTO tblA
SELECT 1,'01012012',NULL UNION ALL
SELECT 1,'01022012',80 UNION ALL
SELECT 1,'01032012',NULL UNION ALL
SELECT 2,'01012012',NULL UNION ALL
SELECT 2,'01022012',10
INSERT INTO tblB
SELECT 1,1,'sampledata' UNION ALL
SELECT 2,2,'data1'
Then find the unique columns :
DECLARE #cols VARCHAR(MAX)
;WITH CTE
AS
(
SELECT
ROW_Number() OVER(PARTITION BY tblA.Fiscal_Period ORDER BY tblA.Fiscal_Period) AS RowNbr,
tblA.Fiscal_Period
FROM
tblA AS tblA
)
SELECT
#cols = COALESCE(#cols + ','+QUOTENAME('FiscalPeriod('+Fiscal_Period+')'),
QUOTENAME('FiscalPeriod('+Fiscal_Period+')'))
FROM
CTE
WHERE
CTE.RowNbr=1
Then execute a pivot with dynamic sql:
DECLARE #query NVARCHAR(4000)=
N'SELECT
*
FROM
(
SELECT
tblB.Row_ID,
tblb.StaticData,
''FiscalPeriod(''+tblA.Fiscal_Period+'')'' AS Name,
tblA.Amount
FROM
tblA AS tblA
JOIN tblB AS tblB
ON tblA.Auto_ID=tblB.Auto_ID
) AS p
PIVOT
(
SUM(Amount)
FOR Name IN ('+#cols+')
) AS Pvt'
EXECUTE(#query)
Then in my case I will drop the temp tables:
DROP TABLE tblA
DROP TABLE tblB
I hope this will help you

Since you didn't specify a flavour of database, please mind, that the following is valid only for MySQL!
A cross-tab query is possible only with a very dirty trick, it is only practically feasable in a stored proc.
You start by thinking out some way, to transform a list of fiscal periods into SQL, something like
SELECT
TABLEB.Row_ID,
TABLEB.staticdata
,fp01012012.Amount as fp01012012amount
,fp01022012.Amount as fp01022012amount
,fp01032012.Amount as fp01032012amount
FROM
TABLEB
LEFT JOIN TableA AS fp01012012 ON fp01012012.Auto_ID=TABLEB.Auto_ID AND fp01012012.Fiscal_Period='01012012'
LEFT JOIN TableA AS fp01022012 ON fp01022012.Auto_ID=TABLEB.Auto_ID AND fp01022012.Fiscal_Period='01022012'
LEFT JOIN TableA AS fp01032012 ON fp01032012.Auto_ID=TABLEB.Auto_ID AND fp01032012.Fiscal_Period='01032012'
Which you now have to build as dynamic SQL - this is feasable only in a stored proc.
DELIMITER $$
DROP PROCEDURE IF EXISTS `create_fiscal_data`$$
CREATE PROCEDURE `create_fiscal_data` ()
BEGIN
DECLARE dynfields VARCHAR(10000) DEFAULT 'SELECT TABLEB.Row_ID, TABLEB.staticdata';
DECLARE dynfrom VARCHAR(10000) DEFAULT ' FROM TABLEB';
DECLARE period VARCHAR(10) DEFAULT '';
DECLARE done INT DEFAULT 0;
DECLARE id INT DEFAULT 7;
DECLARE periods CURSOR FOR SELECT DISTINCT Fiscal_Period FROM TableA;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1;
OPEN periods;
cycleperiods: LOOP
FETCH periods INTO period;
IF done=1 THEN LEAVE cycleperiods; END IF;
SET dynfields=CONCAT(dynfields,',`fp',period,'`.Amount AS `fp',period,'amount`');
SET dynfrom=CONCAT(dynfrom,' LEFT JOIN TableA AS `fp',period,'` ON `fp',period,'`.Auto_ID=TABLEB.Auto_ID AND `fp',period,'`.Fiscal_Period="',period,'"');
END LOOP;
CLOSE periods;
SELECT #dynsql:=CONCAT(dynfields,dynfrom) INTO dynfields;
-- Here comes the trick!
PREPARE dynqry FROM #dynsql;
EXECUTE dynqry;
END$$
DELIMITER ;
The trick is, to build the SQL into the variable #dynsql (DECLAREd variables won't work), then prepare and execute it.
Now the query
CALL `create_fiscal_data;`
Will create the output you need.

Related

SQL Query to update or insert row with If Exists statement not working

previously while posting i missed out table b which hold urn and org_ref . I have three tables. Table a ,b and c, where I have to update the column "total Emp" of table c based on the URN Key in table a. Table B doesnt have URN column but it has Org_ref which so to connect the table a and table c we need to join table b on a.urn=b.urn. I have written a query to loop through the record and do the insert or update, but it's not working and doesn't update. Just insert with blank "Total employee" in table b.
table a
|urn |total employee
|333 |25
|123 |26
|21 |2
|11 |23
Table B
|urn |org_ref
|333 |1234
|123 |343
|21 |2
|11 |23
Table C
org_ref total employe
1234 0 --should update from table a
343 0 --should update from table a
--add record from table a org_ref 21
--add record from table a org_ref 11
Here is my script
Create table #orgs(
iorgrowid int identity (1,1),
ORG_REF varchar(255),
Total_Leader int,
urn int
)
insert into #orgs
select ORG.organisation_ref,s.[TOTAL_LEADERSHIP_ROLE],s.urn
from DiTestDatabase.dbo.organisation org
JOIN DataConversion.dbo.School_plus_download_NI s on org.total_headcount=s.urn
DECLARE
#iReturnCode int,
#iNextcodeRowId int,
#icodeRowId int,
#iCurrentcodeRowId int,
#iNextattRowId int,
#iCurrentattRowId int,
#icodeLoopControl int,
#ORGREF INT,
#counter int,
#Ly_employees int,
#orgnamecount int,
#add1count int,
#urn int
SELECT #iCodeLoopControl = 1
SELECT #iNextCodeRowId = MIN(iorgrowId)
FROM #orgs
IF ISNULL(#iNextCodeRowId,0) = 0
BEGIN
SELECT 'No data in found in table!'
RETURN
END
SELECT
#iCurrentcodeRowId = iorgrowId,
#ORGREF=ORG_REF,
#Ly_employees=Total_Leader,
#urn=urn
FROM #orgs
WHERE iorgRowId = #iNextcodeRowId
set #counter = 0
set #orgnamecount=0
set #add1count=0
--Start the main processing loop.
WHILE #iCodeLoopControl = 1
BEGIN
begin
set #counter = #counter + 1
print 'The counter is ' + cast(#counter as char) + cast(#ORGREF as char)
end
IF EXISTS (SELECT OG.ORGANISATION_REF
FROM ORG_ACCOUNT OG WHERE OG.ORGANISATION_REF= #ORGREF
)
BEGIN
UPDATE ORG_ACCOUNT
SET LY_EMPLOYEES= (SELECT SP.TOTAL_LEADERSHIP_ROLE
FROM DataConversion.dbo.School_plus_download_NI SP
JOIN ORGANISATION ORG ON ORG.TOTAL_HEADCOUNT=SP.URN
JOIN ORG_ACCOUNT OA ON OA.ORGANISATION_REF=ORG.ORGANISATION_REF
WHERE OA.ORGANISATION_REF=#ORGREF)
FROM DataConversion.dbo.School_plus_download_NI SP WHERE SP.URN=#urn
END
ELSE
BEGIN
alter table ditestdatabase.dbo.org_account disable trigger all
INSERT INTO ORG_ACCOUNT
(ORGANISATION_REF,LY_TURNOVER,LY_PROFIT,LY_REMUN,LY_EMPLOYEES,PY_TURNOVER,PY_PROFIT,PY_REMUN,PY_EMPLOYEES,UK_TURNOVER,UK_PROFIT,UK_REMUN,UK_EMPLOYEES,CREATED_BY,
CREATE_TIMESTAMP,UPDATED_BY,UPDATE_TIMESTAMP,PY_POT_SUB,LY_POT_SUB,LY_DATE,PY_DATE,UK_DATE)
VALUES(#ORGREF,NULL,NULL,NULL,#Ly_employees,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,null,GETDATE(),null,null,null,null,null,null,null)
set #orgref=(select organisation_ref from DiTestDatabase.dbo.ORG_ACCOUNT where ORGANISATION_REF=#ORGREF)
end
alter table ditestdatabase.dbo.org_account enable trigger all
SELECT #iNextCodeRowId = NULL
--Get the next iRowId.
SELECT #iNextcodeRowId = MIN(iorgrowId)
FROM #orgs
WHERE iorgrowId > #iCurrentcodeRowId
--Did we get a valid next row id?
IF ISNULL(#iNextCodeRowId,0) = 0
BEGIN
BREAK
END
SELECT #iCurrentcodeRowId = iorgrowId,
#ORGREF=ORG_REF,
#Ly_employees=Total_Leader
FROM #orgs
WHERE iorgRowId = #iNextcodeRowId
END
DROP TABLE #orgs
RETURN
Solution :- As i dont have anything common between table a and c hence i have to take help of table b where the only common thing between a and b is column URN and the only column common between b and c is Org_ref. I have made the changes on my update as
SET LY_EMPLOYEES= (SELECT SP.TOTAL_LEADERSHIP_ROLE
FROM DataConversion.dbo.School_plus_download_NI SP
JOIN ORGANISATION ORG ON ORG.TOTAL_HEADCOUNT=SP.URN
JOIN ORG_ACCOUNT OA ON OA.ORGANISATION_REF=ORG.ORGANISATION_REF
WHERE sp.URN = #urn) --#ORGREF)
FROM ORG_ACCOUNT WHERE ORGANISATION_REF=#ORG_REF
Which solved the issue.
Thanks all
is this helpful.?
CREATE TABLE #a(urn bigint, total_employe bigint)
CREATE TABLE #b(urn bigint,org_ref bigint)
CREATE TABLE #c(org_ref bigint,total_employe bigint)
INSERT INTO #a
SELECT 333,25 Union ALL
SELECT 123,26 Union ALL
SELECT 21,2 Union ALL
SELECT 11,23
INSERT INTO #b
SELECT 333,1234 Union ALL
SELECT 123,343 Union ALL
SELECT 21,2 Union ALL
SELECT 11,23
INSERT INTO #c
SELECT 1234,0 Union ALL
SELECT 343,0
;with cte
AS
(
SELECT b.org_ref,a.total_employe from #a a
INNER JOIN #b b on a.urn=b.urn
)
MERGE INTO #c c
USING cte on c.org_ref=cte.org_ref
WHEN MATCHED THEN
UPDATE
SET c.total_employe=cte.total_employe
WHEN NOT MATCHED THEN
INSERT(org_ref,total_employe)
VALUES(org_ref,total_employe);
SELECT * FROM #c
DROP TABLE #a
DROP TABLE #b
DROP TABLE #c
Do not use a while loop or a cursor for a simple upsert operation! You will find everywhere on the web that it is counterproductive!
I will answer in the simpler terms of table a/b:
-- First, update the ones that exist
update alias_b -- Note that you NEED an alias to update using join
set [total employee]=a.[total employee]
from a
inner join b alias_b on a.org_ref=alias_b.org_ref
-- Then, insert the ones that don't
insert b
select (the columns you want)
from a
where not exists (select 1 from b where a.org_ref=b.org_ref)
--UPDATE
UPDATE B SET B.TOTAL_LEADER = A.TOTAL_LEADER FROM TABLEA A INNER JOIN TABLEB B ON
A.ORG_REF = B.ORG_REF
--INSERT
INSERT INTO TABLEB(ORG_REF,URN,TOTAL_LEADER)
SELECT A.ORG_REF,A.URN,A.TOTAL_LEADER FROM TABLEA A LEFT OUTER JOIN TABLEB B ON
A.ORG_REF = B.ORG_REF WHERE B.ORG_REF IS NULL
I Think you are looking for this one.
How about an SELECT INTO? Something like:
SELECT b.org_ref, a.totalemployee INTO c FROM a LEFT JOIN b ON (a.urn = b.urn)

Fetch specific column and row data based on row values from another table in SQL server

Does anyone know if it is possible to fetch data from a column in a table based on row values from another table?
e.g.
Table 1:
Name| Date
----|-----
Bob | D1
Jon | D2
Stu | D3
Amy | D4
Table 2:
Date |Bob |Jon |Stu |Amy
-----|----|----|----|----
D1 | A | B | C | D
D2 | B | C | D | A
D3 | C | D | A | B
D4 | D | A | B | C
I need to match the date but bring through the correct letter for each name
So Table 3 would be:
Name| Date | Letter
----|------|-------
Bob | D1 | A
Jon | D2 | C
Stu | D3 | A
Amy | D4 | C
Any suggestions are welcome.
thanks
If you are looking for way without column hardcodes, you can try this.
Lets your tables has names #table1, #table2. Then:
select
[Name] = [t1].[Name]
,[Date] = [t1].[Date]
,[Letter] = [col2].[value]('.', 'varchar(10)')
from
#table1 as [t1]
cross apply
(
select [t2_xml] = cast((select * from #table2 for xml path('t2')) as xml)
) as [t2]
cross apply
[t2].[t2_xml].[nodes]('t2[Date/text()=sql:column("[t1].[Date]")]') as [tab]([col])
cross apply
[col].[nodes]('*[local-name(.)=sql:column("[t1].[Name]")]') as [tab2]([col2]);
There are many ways to achieve the desired output. My solution uses a combination of cursors and dynamic TSQL.
Here is the code, commented step by step:
--1. create test tables
create table table1 ([Name] nvarchar(50),[Date] nvarchar(50))
create table table2 ([Date] nvarchar(50),Bob nvarchar(50),Jon nvarchar(50),Stu nvarchar(50),Amy nvarchar(50))
create table table3 ([Name] nvarchar(50),[Date] nvarchar(50),[Letter] nvarchar(50))
--2. populate test tables
insert into table1
select 'Bob','D1'
union all select 'Jon','D2'
union all select 'Stu','D3'
union all select 'Amy','D4'
insert into table2
select 'D1','A','B','C','D'
union all select 'D2','B','C','D','A'
union all select 'D3','C','D','A','B'
union all select 'D4','D','A','B','C'
--3. declare variables
DECLARE #query NVARCHAR(max); --this variable will hold the dynamic TSQL query
DECLARE #name NVARCHAR(50);
DECLARE #date NVARCHAR(50);
DECLARE #result NVARCHAR(50) --this variable will hold "letter" value returned by dynamic TSQL query
DECLARE #testCursor CURSOR;
--4. define the cursor that will scan all rows in table1
SET #testCursor = CURSOR FOR SELECT [Name], [Date] FROM table1;
OPEN #testCursor;
FETCH NEXT FROM #testCursor INTO #name, #date;
WHILE ##FETCH_STATUS = 0
BEGIN
--5. for each row in table 1 create a dynamic query that retrieves the correct "Letter" value from table2
set #query = 'select #res=' + #name + ' from table2 where [Date] =''' + #date +''''
--6. executes dynamic TSQL query saving result in #result variable
EXECUTE sp_executesql #query, N'#res nvarchar(50) OUTPUT', #res=#result OUTPUT
--inserts data in table3 that holds final results
insert into table3 select #name, #date, #result
FETCH NEXT FROM #testCursor INTO #name, #date;
END
CLOSE #testCursor;
DEALLOCATE #testCursor;
select * from table1
select * from table2
select * from table3
Here are the results. The first two tables show the inputs, the third table contains the actual results:

Resultset with pivot table as sub query - MSSQL

I have two tables with following structure.
and
and I want results like this from one query.
CUSTOMER_CODE | CUSTOMER_NAME | LINE 1 |LINE 2 | LINE 3
we have to make first table as pivot, but how not sure.
Please advise.
Thanks
Here is a dynamic conditional aggregation There were no table names, so TABLE1 relates to Image1
Declare #SQL varchar(max)=''
Select #SQL = #SQL+',[Line '+cast([Line#] as varchar(25))+']=max(case when [Line#]='+cast([Line#] as varchar(25))+' then EMail else '''' end)'
From (Select Distinct [Line#] from Table1) A
Order By [Line#]
Select #SQL='
Select A.Customer_Code
,B.Customer_Branch_Name'+#SQL+'
From Table1 A
Join Table2 B
on A.Customer_Code=B.Customer_Branch
Group By A.Customer_Code,B.Customer_Branch_Name'
Exec(#SQL)
Returns

Create a view based on column metadata

Let's assume two tables:
TableA holds various data measurements from a variety of stations.
TableB holds metadata, about the columns used in TableA.
TableA has:
stationID int not null, pk
entryDate datetime not null, pk
waterTemp float null,
waterLevel float null ...etc
TableB has:
id int not null, pk, autoincrement
colname varchar(50),
unit varchar(50) ....etc
So for example, one line of data from tableA reads:
1 | 2013-01-01 00:00 | 2.4 | 3.5
two lines from tableB read:
1| waterTemp | celcius
2| waterLevel | meters
This is a simplified example. In truth, tableA might hold close to 20 different data columns, and table b has close to 10 metadata columns.
I am trying to design a view which will output the results like this:
StationID | entryDate | water temperature | water level |
1 | 2013-01-01 00:00 | 2.4 celcius | 3.5 meters |
So two questions:
Other than specifying subselects from TableB (..."where
colname='XXX'") for each column, which seems horribly insufficient
(not to mention...manual :P ), is there a way to get the result I
mentioned earlier with automatic match on colname?
I have a hunch
that this might be bad design on the database. Is it so? If yes,
what would be a more optimal design? (Bear in mind the complexity of
the data structure I mentioned earlier)
dynamic SQL with PIVOT is the answer. though it is dirty in terms of debugging or say for some new developer to understand the code but it will give you the result you expected.
check the below query.
in this we need to prepare two things dynamically. one is list columns in the result set and second is list of values will appear in PIVOT query. notice in the result i do not have NULL values for Column3, Column5 and Column6.
SET NOCOUNT ON
IF OBJECT_ID('TableA','u') IS NOT NULL
DROP TABLE TableA
GO
CREATE TABLE TableA
(
stationID int not null IDENTITY (1,1)
,entryDate datetime not null
,waterTemp float null
,waterLevel float NULL
,Column3 INT NULL
,Column4 BIGINT NULL
,Column5 FLOAT NULL
,Column6 FLOAT NULL
)
GO
IF OBJECT_ID('TableB','u') IS NOT NULL
DROP TABLE TableB
GO
CREATE TABLE TableB
(
id int not null IDENTITY(1,1)
,colname varchar(50) NOT NULL
,unit varchar(50) NOT NULL
)
INSERT INTO TableA( entryDate ,waterTemp ,waterLevel,Column4)
SELECT '2013-01-01',2.4,3.5,101
INSERT INTO TableB( colname, unit )
SELECT 'WaterTemp','celcius'
UNION ALL SELECT 'waterLevel','meters'
UNION ALL SELECT 'Column3','unit3'
UNION ALL SELECT 'Column4','unit4'
UNION ALL SELECT 'Column5','unit5'
UNION ALL SELECT 'Column6','unit6'
DECLARE #pvtInColumnList NVARCHAR(4000)=''
,#SelectColumnist NVARCHAR(4000)=''
, #SQL nvarchar(MAX)=''
----getting the list of Columnnames will be used in PIVOT query list
SELECT #pvtInColumnList = CASE WHEN #pvtInColumnList=N'' THEN N'' ELSE #pvtInColumnList + N',' END
+ N'['+ colname + N']'
FROM TableB
--PRINT #pvtInColumnList
----lt and rt are table aliases used in subsequent join.
SELECT #SelectColumnist= CASE WHEN #SelectColumnist = N'' THEN N'' ELSE #SelectColumnist + N',' END
+ N'CAST(lt.'+sc.name + N' AS Nvarchar(MAX)) + SPACE(2) + rt.' + sc.name + N' AS ' + sc.name
FROM sys.objects so
JOIN sys.columns sc
ON so.object_id=sc.object_id AND so.name='TableA' AND so.type='u'
JOIN TableB tbl
ON tbl.colname=sc.name
JOIN sys.types st
ON st.system_type_id=sc.system_type_id
ORDER BY sc.name
IF #SelectColumnist <> '' SET #SelectColumnist = N','+#SelectColumnist
--PRINT #SelectColumnist
----preparing the final SQL to be executed
SELECT #SQL = N'
SELECT
--this is a fixed column list
lt.stationID
,lt.entryDate
'
--dynamic column list
+ #SelectColumnist +N'
FROM TableA lt,
(
SELECT * FROM
(
SELECT colname,unit
FROM TableB
)p
PIVOT
( MAX(p.unit) FOR p.colname IN ( '+ #pvtInColumnList +N' ) )q
)rt
'
PRINT #SQL
EXECUTE sp_executesql #SQL
here is the result
ANSWER to your Second Question.
the design above is not even giving performance nor flexibility. if user wants to add new Metadata (Column and Unit) that can not be done w/o changing table definition of TableA.
if we are OK with writing Dynamic SQL to give user Flexibility we can redesign the TableA as below. there is nothing to change in TableB. I would convert it in to Key-value pair table. notice that StationID is not any more IDENTITY. instead for given StationID there will be N number of row where N is the number of column supplying the Values for that StationID. with this design, tomorrow if user adds new Column and Unit in TableB it will add just new Row in TableA. no table definition change required.
SET NOCOUNT ON
IF OBJECT_ID('TableA_New','u') IS NOT NULL
DROP TABLE TableA_New
GO
CREATE TABLE TableA_New
(
rowID INT NOT NULL IDENTITY (1,1)
,stationID int not null
,entryDate datetime not null
,ColumnID INT
,Columnvalue NVARCHAR(MAX)
)
GO
IF OBJECT_ID('TableB_New','u') IS NOT NULL
DROP TABLE TableB_New
GO
CREATE TABLE TableB_New
(
id int not null IDENTITY(1,1)
,colname varchar(50) NOT NULL
,unit varchar(50) NOT NULL
)
GO
INSERT INTO TableB_New(colname,unit)
SELECT 'WaterTemp','celcius'
UNION ALL SELECT 'waterLevel','meters'
UNION ALL SELECT 'Column3','unit3'
UNION ALL SELECT 'Column4','unit4'
UNION ALL SELECT 'Column5','unit5'
UNION ALL SELECT 'Column6','unit6'
INSERT INTO TableA_New (stationID,entrydate,ColumnID,Columnvalue)
SELECT 1,'2013-01-01',1,2.4
UNION ALL SELECT 1,'2013-01-01',2,3.5
UNION ALL SELECT 1,'2013-01-01',4,101
UNION ALL SELECT 2,'2012-01-01',1,3.6
UNION ALL SELECT 2,'2012-01-01',2,9.9
UNION ALL SELECT 2,'2012-01-01',4,104
SELECT * FROM TableA_New
SELECT * FROM TableB_New
SELECT *
FROM
(
SELECT lt.stationID,lt.entryDate,rt.Colname,lt.Columnvalue + SPACE(3) + rt.Unit AS ColValue
FROM TableA_New lt
JOIN TableB_new rt
ON lt.ColumnID=rt.ID
)t1
PIVOT
(MAX(ColValue) FOR Colname IN ([WaterTemp],[waterLevel],[Column1],[Column2],[Column4],[Column5],[Column6]))pvt
see the result below.
I would design this database like the following:
A table MEASUREMENT_DATAPOINT that contains the measured data points. It would have the columns ID, measurement_id, value, unit, name.
One entry would be 1, 1, 2.4, 'celcius', 'water temperature'.
A table MEASUREMENTS that contains the data of the measurement itself. Columns: ID, station_ID, entry_date.
You might want to look into the MS-SQL function called PIVOT/UNPIVOT
http://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx
you can take column names and have them in rows or vice versa using this command.
Once you have the column name in the column itself you can join that column from tableA to tableB. Then unpivot to get your data back the way you want it. (caveat I may be swapping the use of pivot and unpivot :))
Word to the wise though, if you are working with large tables, pivot is not the fastest of operations.
I think you would have to flip it to a row per metric. Looking at your design above:
1 | 2013-01-01 00:00 | 2.4 | 3.5
How do I know what row in table b that applies to?
I would try something like this:
Table B:
Metric_Key | Metric
1 | WaterLevel in Meters
2 | Temp in Celcius
...
Table A:
StationID | entrydate | Metric_Key | Value
1 2013-01-01 00:00 1 2.4

SQL grouping by all the columns

Is there any way to group by all the columns of a table without specifying the column names? Like:
select * from table group by *
The DISTINCT Keyword
I believe what you are trying to do is:
SELECT DISTINCT * FROM MyFooTable;
If you group by all columns, you are just requesting that duplicate data be removed.
For example a table with the following data:
id | value
----+----------------
1 | foo
2 | bar
1 | foo
3 | something else
If you perform the following query which is essentially the same as SELECT * FROM MyFooTable GROUP BY * if you are assuming * means all columns:
SELECT * FROM MyFooTable GROUP BY id, value;
id | value
----+----------------
1 | foo
3 | something else
2 | bar
It removes all duplicate values, which essentially makes it semantically identical to using the DISTINCT keyword with the exception of the ordering of results. For example:
SELECT DISTINCT * FROM MyFooTable;
id | value
----+----------------
1 | foo
2 | bar
3 | something else
If you are using SqlServer the distinct keyword should work for you. (Not sure about other databases)
declare #t table (a int , b int)
insert into #t (a,b) select 1, 1
insert into #t (a,b) select 1, 2
insert into #t (a,b) select 1, 1
select distinct * from #t
results in
a b
1 1
1 2
I wanted to do counts and sums over full resultset. I achieved grouping by all with GROUP BY 1=1.
Short answer: no. GROUP BY clauses intrinsically require order to the way they arrange your results. A different order of field groupings would lead to different results.
Specifying a wildcard would leave the statement open to interpretation and unpredictable behaviour.
nope. are you trying to do some aggregation? if so, you could do something like this to get what you need
;with a as
(
select sum(IntField) as Total
from Table
group by CharField
)
select *, a.Total
from Table t
inner join a
on t.Field=a.Field
No because this fundamentally means that you will not be grouping anything. If you group by all columns (and have a properly defined table w/ a unique index) then SELECT * FROM table is essentially the same thing as SELECT * FROM table GROUP BY *.
Here is my suggestion:
DECLARE #FIELDS VARCHAR(MAX), #NUM INT
--DROP TABLE #FIELD_LIST
SET #NUM = 1
SET #FIELDS = ''
SELECT
'SEQ' = IDENTITY(int,1,1) ,
COLUMN_NAME
INTO #FIELD_LIST
FROM Req.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = N'new340B'
WHILE #NUM <= (SELECT COUNT(*) FROM #FIELD_LIST)
BEGIN
SET #FIELDS = #FIELDS + ',' + (SELECT COLUMN_NAME FROM #FIELD_LIST WHERE SEQ = #NUM)
SET #NUM = #NUM + 1
END
SET #FIELDS = RIGHT(#FIELDS,LEN(#FIELDS)-1)
EXEC('SELECT ' + #FIELDS + ', COUNT(*) AS QTY FROM [Req].[dbo].[new340B] GROUP BY ' + #FIELDS + ' HAVING COUNT(*) > 1 ')
You can use Group by All but be careful as Group by All will be removed from future versions of SQL server.