disaggregate summarised table in SQL Server 2008 - sql

I've received data from an external source, which is in a summarised format. I need a way to disaggregate this to fit into a system I am using.
To illustrate, suppose the data I received looks like this:
receivedTable:
Age Gender Count
40 M 3
41 M 2
I want this is a disaggregated format like this:
systemTable:
ID Age Gender
1 40 M
2 40 M
3 40 M
4 41 M
5 41 M
Thanks
Karl

Depending of the range of your count you could use a lookup table that holds exactly x records for each integer x. Like this:
create table counter(num int)
insert into counter select 1
insert into counter select 2
insert into counter select 2
insert into counter select 3
insert into counter select 3
insert into counter select 3
insert into counter select 4
insert into counter select 4
insert into counter select 4
insert into counter select 4
then join with this table:
create table source(age int, gender char(1), num int)
insert into source select 40, 'm', 3
insert into source select 30, 'f', 2
insert into source select 20, 'm', 1
--insert into destination(age, gender)
select age, gender
from source
inner join counter on counter.num = source.num

From the "Works on my machine (TM)" stable a recursive query, with all the usual caveats about maximum recursion depth.
with Expanded(exAge, exGender, exRowIndex) as
(
select
Age as exAge,
Gender as exGender,
1 as exRowIndex
from
tblTest1
union all
select
exAge,
exGender,
exRowIndex+1
from
tblTest1 t1
inner join
Expanded e on (e.exAge = t1.Age and e.exGender = t1.Gender and e.exRowIndex < t1.Count)
)
select
exAge,
exGender,
exRowIndex
from
Expanded
order by
exAge,
exGender,
exRowIndex
option (MAXRECURSION 0) -- BE CAREFUL!!
You don't get the row identifier - but inserting the result of the query into a table with an identity column would deal with that.

Related

T-SQL Select to compute a result row on preceeding group/condition

How to achieve this result using a T-SQL select query.
Given this sample table :
create table sample (a int, b int)
insert into sample values (999, 10)
insert into sample values (16, 11)
insert into sample values (10, 12)
insert into sample values (25, 13)
insert into sample values (999, 20)
insert into sample values (14, 12)
insert into sample values (90, 45)
insert into sample values (18, 34)
I'm trying to achieve this output:
a b result
----------- ----------- -----------
999 10 10
16 11 10
10 12 10
25 13 10
999 20 20
14 12 20
90 45 20
18 34 20
The rule is fairly simple: if column 'a' has the special value of 999 the result for that row and following rows (unless the value of 'a' is again 999) will be the value of column 'b'. Assume the first record will have 999 on column 'a'.
Any hint how to implement, if possible, the select query without using a stored procedure or function?
Thank you.
António
You can do what you want if you add a column to specify the ordering:
create table sample (
id int identity(1, 1),
a int,
b int
);
Then you can do what you want by finding the "999" version that is most recent and copying that value. Here is a method using window functions:
select a, b, max(case when a = 999 then b end) over (partition by id_999) as result
from (select s.*,
max(case when a = 999 then id end) over (order by id) as id_999
from sample s
) s;
You need to have an id column
select cn.id, cn.a
, (select top (1) b from sample where sample.id <= cn.id and a = 999 order by id desc)
from sample as cn
order by id

SQL: how to select common lines of one table on several column

I have a table :
create table a (page int, pro int)
go
insert into a select 1, 2
insert into a select 4, 2
insert into a select 5, 2
insert into a select 9, 2
insert into a select 1, 3
insert into a select 2, 3
insert into a select 3, 3
insert into a select 4, 3
insert into a select 9, 3
insert into a select 1, 4
insert into a select 9, 4
insert into a select 12, 4
insert into a select 1, 5
insert into a select 9, 5
insert into a select 12, 5
insert into a select 13, 5
insert into a select 14, 5
insert into a select 15, 5
go
(here is the SQLfiddle of this table and queries I began to write )
Common value of page on ALL lines
I'm looking to extract the common column "page" for each column "pro" from this table.
here is what we expect :
1
9
I tried to use:
SELECT DISTINCT a.page
FROM a
WHERE a.page IN (
SELECT b.page FROM a as b
WHERE b.pro <> a.pro
)
but this query returns every "page" that have at least one common values which is not what we need to have. see below :
1
4
9
12
The opposite query aka different value at least one but not all time
I'm looking to extract the "page" linked to one or more "pro" but without being common to all of them (it's the exact opposite of the previous query)
Here is what we expect :
2
3
4
5
12
13
14
15
I can't manage to find a solution to those 2 queries :'(
Could anyone help me on those ones?
Best regards
edit: the SQLfiddle url
Just a bit of reversed thinking - group by page and count distinct pro values for each. Return rows that matches the total of distinct pro values
SELECT [page]
FROM a
GROUP BY [page]
HAVING COUNT(DISTINCT pro) = (SELECT COUNT(DISTINCT pro) FROM a)
SQLFiddle
EDIT: for the second problem, just replace = with '<' in the final line -> SQLFiddle
Fot the first part of the question, try this query:
SELECT DISTINCT t1.page FROM a t1
WHERE (SELECT COUNT(DISTINCT t2.pro) FROM a t2 WHERE
t2.page = t1.page) =
(SELECT COUNT(DISTINCT t3.pro) FROM a t3)
And the second query is the simple substraction from all page values:
SELECT DISTINCT t4.page FROM a t4
EXCEPT
SELECT DISTINCT t1.page FROM a t1
WHERE (SELECT COUNT(DISTINCT t2.pro) FROM a t2 WHERE
t2.page = t1.page) =
(SELECT COUNT(DISTINCT t3.pro) FROM a t3)

How to insert multiple rows from one column?

I want to insert multiple rows from one column by splitting column value. But I have to do that without cursors because of performance issues.
Every value is splitted to 6 chars length values. Then these values also splitted to 3, 1 and 2 chars length values to insert different columns in table B.
I think giving a sample will clarify my question:
Table A
ID Value
1 ABCDEFGHJKLM
2 NOPRST
3 NULL VALUE
I want to insert these values into table B like this format
Table B
ID Value1 Value2 Value3
1 ABC D EF
1 GHJ K LM
2 NOP R ST
Supposing 600(100 rows) as maximum length of value:
insert into tableB
select id, substr(value,n*6+1,3), substr(value,n*6+4,1), substr(value,n*6+5,2)
from tableA
join (select level-1 as n from dual connect by level <= 100)
on length(value) > n*6;
see Sqlfiddle.
select ID,
SUBSTR(value,number*6+1,3),
SUBSTR(value,number*6+4,1),
SUBSTR(value,number*6+5,2)
from yourtable,
(select 0 as number union select 1 union select 2 union select 3 union select 4
union select 5 union select 6) as numbers
/* etc up to the max length of your string /6 */
where LEN(value)>number*6
try this:
Please convert it to ORACLE SQL..
Even though, its using a while loop, its doing bulk inserts..and the loop is executed as per the length of maximun length of value in the table
declare #max_len int=0;
declare #counter int=0;
declare #col_index int=1;
select #max_len=MAX(len(Value)) from TableA
while (#max_len/6 > #counter)
begin
set #counter=#counter+1
Insert into TableB
select ID,substring(Value,#col_index,3),
substring(Value,#col_index+3,1),
substring(Value,#col_index+4,2)
from TableA where substring(Value,#col_index,3) is not null
set #col_index=#col_index+6
end

SQL if statement with two tables

Given the following tables:
table objects
id Name rating
1 Megan 9
2 Irina 10
3 Vanessa 7
4 Samantha 9
5 Roxanne 1
6 Sonia 8
swap table
id swap_proposalid counterpartyid
1 4 2
2 3 2
Everyone wants the ten. I would like to make a list for Irina of possible swaps where id 4 and 3 don't appear because the propositions are already there.
output1
id Name rating
1 Megan 9
5 Roxanne 1
6 Sonia 8
Thanks
This should do the trick:
SELECT o.id, o.Name, o.rating
FROM objects o
LEFT JOIN swap s on o.id = s.swap_proposalid
WHERE s.id IS NULL
AND o.Name != 'Irina'
This works
SELECT mt2.ID, mt2.Name, mt2.Rating
FROM [MyTable] mt2 -- Other Candidates
, [MyTable] mt1 -- Candidate / Subject (Irina)
WHERE mt2.ID NOT IN
(
SELECT st.swap_proposalid
FROM SwapTable st
WHERE
st.counterpartyid = mt1.ID
)
AND mt1.ID <> mt2.ID -- Don't match Irina with Irina
AND mt1.Name = 'Irina' -- Find other swaps for Irina
-- Test Data
CREATE TABLE MyTable
(
ID INT,
Name VARCHAR(100),
Rating INT
)
GO
CREATE TABLE SwapTable
(
ID INT,
swap_proposalid INT,
counterpartyid INT
)
GO
INSERT INTO MyTable VALUES(1 ,'Megan', 9)
INSERT INTO MyTable VALUES(2 ,'Irina', 10)
INSERT INTO MyTable VALUES(3 ,'Vanessa', 7)
INSERT INTO MyTable VALUES(4 ,'Samantha', 9)
INSERT INTO MyTable VALUES(5 ,'Roxanne', 1)
INSERT INTO MyTable VALUES(6 ,'Sonia', 8)
INSERT INTO SwapTable(ID, swap_proposalid, counterpartyid)
VALUES (1, 4, 2)
INSERT INTO SwapTable(ID, swap_proposalid, counterpartyid)
VALUES (1, 3, 2)
Guessing that the logic involves identifying the objects EXCEPT the highest rated object EXCEPT propositions with the highest rated object e.g. (using sample DDL and data kindly posted by #nonnb):
WITH ObjectHighestRated
AS
(
SELECT ID
FROM MyTable
WHERE Rating = (
SELECT MAX(T.Rating)
FROM MyTable T
)
),
PropositionsForHighestRated
AS
(
SELECT swap_proposalid AS ID
FROM SwapTable
WHERE counterpartyid IN (SELECT ID FROM ObjectHighestRated)
),
CandidateSwappersForHighestRated
AS
(
SELECT ID
FROM MyTable
EXCEPT
SELECT ID
FROM ObjectHighestRated
EXCEPT
SELECT ID
FROM PropositionsForHighestRated
)
SELECT *
FROM MyTable
WHERE ID IN (SELECT ID FROM CandidateSwappersForHighestRated);

Pivot values of a column based on a search string

Note: I would like to do this in a single SQL statement. not pl/sql, cursor loop, etc.
I have data that looks like this:
ID String
-- ------
01 2~3~1~4
02 0~3~4~6
03 1~4~5~1
I want to provide a report that somehow pivots the values of the String column into distinct rows such as:
Value "Total number in table"
----- -----------------------
1 3
2 1
3 2
4 3
5 1
6 1
How do I go about doing this? It's like a pivot table but I am trying to pivot the data in a column, rather than pivoting the columns in the table.
Note that in real application, I do not actually know what the values of the String column are; I only know that the separation between values is '~'
Given this test data:
CREATE TABLE tt (ID INTEGER, VALUE VARCHAR2(100));
INSERT INTO tt VALUES (1,'2~3~1~4');
INSERT INTO tt VALUES (2,'0~3~4~6');
INSERT INTO tt VALUES (3,'1~4~5~1');
This query:
SELECT VALUE, COUNT(*) "Total number in table"
FROM (SELECT tt.ID, SUBSTR(qq.value, sp, ep-sp) VALUE
FROM (SELECT id, value
, INSTR('~'||value, '~', 1, L) sp -- 1st posn of substr at this level
, INSTR(value||'~', '~', 1, L) ep -- posn of delimiter at this level
FROM tt JOIN (SELECT LEVEL L FROM dual CONNECT BY LEVEL < 20) q -- 20 is max #substrings
ON LENGTH(value)-LENGTH(REPLACE(value,'~'))+1 >= L
) qq JOIN tt on qq.id = tt.id)
GROUP BY VALUE
ORDER BY VALUE;
Results in:
VALUE Total number in table
---------- ---------------------
0 1
1 3
2 1
3 2
4 3
5 1
6 1
7 rows selected
SQL>
You can adjust the maximum number of items in your search string by adjusting the "LEVEL < 20" to "LEVEL < your_max_items".