SQL: Making a 'computation row' - sql

I have a table that looks like this
TYPE | A | B | C | ... | Z
one | 4 | 4 | 4 | ... | 4
two | 3 | 2 | 2 | ... | 1
And I wanted to insert a row with a computation (row one minus row two):
TYPE | A | B | C | ... | Z
one | 4 | 4 | 4 | ... | 4
two | 3 | 2 | 2 | ... | 1
delta| 1 | 2 | 2 | ... | 3
I was thinking of a SQL command that looks like
(select A from table where type=one) - (select A from table where type=two)
Down side is, it's too long and I also have to do that for all the columns (A-Z) and that's quite a lot.
I'm sure there's a more elegant way of doing this.
PS:
The sequence of my code looks like this btw:
// I'm inserting the data from a RawTable to a TempTable
INSERT one
INSERT two
INSERT delta
INSERT three
INSERT four
INSERT delta
...
INSERT onehundredone
INSERT onehundredtwo
INSERT delta

I have added an ID column with identity to your temp table. You can use that to figure out what rows should be grouped.
create table YourTable
(
ID int identity primary key,
[TYPE] varchar(20),
A int,
B int,
C int
)
insert into YourTable ([TYPE], A, B, C)
select 'one', 4, 4, 4 union all
select 'two', 3, 2, 2 union all
select 'three', 7, 4, 4 union all
select 'four', 3, 2, 2 union all
select 'five', 8, 4, 4 union all
select 'six', 3, 2, 2
select T.[TYPE], T.A, T.B, T.C
from
(
select
T.ID,
T.[TYPE],
T.A,
T.B,
T.C
from YourTable as T
union all
select
T2.ID,
'delta' as [TYPE],
T1.A-T2.A as A,
T1.B-T2.B as B,
T1.C-T2.C as C
from YourTable as T1
inner join YourTable as T2
on T1.ID = T2.ID-1 and
T2.ID % 2 = 0
) as T
order by T.ID, case T.[TYPE] when 'delta' then 1 else 0 end
Result:
TYPE A B C
-------------------- ----------- ----------- -----------
one 4 4 4
two 3 2 2
delta 1 2 2
three 7 4 4
four 3 2 2
delta 4 2 2
five 8 4 4
six 3 2 2
delta 5 2 2
Sorting on column C from first row in group:
select T.[TYPE], T.A, T.B, T.C
from
(
select
T1.ID,
T1.[TYPE],
case T1.ID % 2 when 1 then T1.C else T2.C end as Sortorder,
T1.A,
T1.B,
T1.C
from YourTable as T1
left outer join YourTable as T2
on T1.ID = T2.ID+1
union all
select
T2.ID,
'delta' as [TYPE],
T1.C as Sortorder,
T1.A-T2.A as A,
T1.B-T2.B as B,
T1.C-T2.C as C
from YourTable as T1
inner join YourTable as T2
on T1.ID = T2.ID-1 and
T2.ID % 2 = 0
) as T
order by T.Sortorder, T.ID, case T.[TYPE] when 'delta' then 1 else 0 end

I'm not aware of any way to do this "easily" (i.e. without having to specify every column), I can't come up with any way to do it easily, so I'll go on the record as saying that it can't be done. Easily.
The non-easy way would be to build dynamic code--something that loops through the database metadata, builds a string containing the statement(s) to execute your desired routine column by column, and then execute that string. You really want to avoid this whenever possible.
One shortcut, if you just need to build a procedure or function that does this (i.e. build once run many), you could copy the list of columns into a spreadsheet (Excel), build out the highly-repetitive statements using forumlas that reference the column names, and then copying the results back. (This is much simpler to do than it is to explain.)

I have no idea why you're doing this, but the way I'd approach it is:
insert into table
select 'delta',
t1.a - t2.a,
t1.b - t2.b
.....
from table t1,
table t2
where t1.type = 'one'
and t2.type = 'two'
You would have to run this query immediately after inserting "one" and "two", then re-run it after inserting "three" and "four". Nasty nasty nasty.
If you can re-name the columns in some way, or create a numerical column, you could run it in a single query.

When you replace one for 1, two for 2, and so on, then maybe this sql could work:
INSERT INTO PodMays
SELECT
"Delta", A.A-B.A, A.B-B.B, A.C-B.C, A.D-B.D, A.E-B.E
FROM
(
SELECT TOP 1
*
FROM
(SELECT TOP 2 * FROM PodMays WHERE Type <> "Delta" ORDER BY Type DESC)
ORDER BY
Type ASC
) AS A,
(
SELECT TOP 1
*
FROM
(SELECT TOP 2 * FROM PodMays WHERE Type <> "Delta" ORDER BY Type DESC)
ORDER BY
Type DESC
) AS B

Related

SELECT all rows where same value in column 'a' and 'b' but do not the same value 'c'

I have a table:
ID | a | b | c |
1 1 1 1
2 1 1 2
3 2 2 1
4 1 2 3
5 2 2 1
I need to select all duplicate rows where I have the same value in column 'a' and 'b' but do not have the same in 'c'
So in this case I should get rows with ID=1,2
I can use GROUP BY to group value 'a' and 'b' and then select it if it count more than 1, but I dont know how to include this check about column 'c'.
Could you help me, please.
I think you want exists:
select t.*
from t
where exists (select 1
from t t2
where t2.a = t.a and t2.b = t.b and
t2.c <> t.c
);
If you just wanted the a/b values, then you could use aggregation:
select a, b
from t
group by a, b
having min(c) <> max(c);
you can also use :
select t1.id from table t1 cross join table t2
where s1.a=s2.a and s1.b=s2.b and s1.c!=s2.c

SQL theory: Filtering out duplicates in one column, picking lowest value in other column

I am trying to figure out the best way to remove rows from a result set where either the value in one column or the value in a different column has a duplicate in the result set.
Imagine the results of a query are as follows:
a_value | b_value
-----------------
1 | 1
2 | 1
2 | 2
3 | 1
4 | 3
5 | 2
6 | 4
6 | 5
What I want to do is:
Eliminate all rows that have duplicate values in a_value
Pick only 1 row for a given b_value
So I'd want the filtered results to end up like this after eliminating a_value duplicates:
a_value | b_value
-----------------
1 | 1
3 | 1
4 | 3
5 | 2
And then like this after picking only a single b_value:
a_value | b_value
-----------------
1 | 1
4 | 3
5 | 2
I'd appreciate suggestions on how to accomplish this task in an efficient way via SQL.
with
q_res ( a_value, b_value ) as (
select 1, 1 from dual union all
select 2, 1 from dual union all
select 2, 2 from dual union all
select 3, 1 from dual union all
select 4, 3 from dual union all
select 5, 2 from dual union all
select 6, 4 from dual union all
select 6, 5 from dual
)
-- end test data; solution begins below
select min(a_value) as a_value, b_value
from (
select a_value, min(b_value) as b_value
from q_res
group by a_value
having count(*) = 1
)
group by b_value
order by a_value -- ORDER BY is optional
;
A_VALUE B_VALUE
------- -------
1 1
4 3
5 2
1) In the inner query I am avoiding all duplicates which are present in a_value
column and getting all the remaining rows from input table and storing them
as t2. By joining t2 with t1 there would be full data without any dups as per
your #1 in requirement.
SELECT t1.*
FROM Table t1,
(
SELECT a_value
FROM Table
GROUP BY a_value
HAVING COUNT(*) = 1
) t2
WHERE t1.a_value = t2.a_value;
2) Once the filtered data is obtained, I am assigning rank to each row in the filtered dataset obtained in step-1 and I am selecting only rows with rank=1.
SELECT X.a_value,
X.b_value
FROM
(
SELECT t1.*,
ROW_NUMBER() OVER ( PARTITION BY t1.b_value ORDER BY t1.a_value,t1.b_value ) AS rn
FROM Table t1,
(
SELECT a_value
FROM Table
GROUP BY a_value
HAVING COUNT(*) = 1
) t2
WHERE t1.a_value = t2.a_value
) X
WHERE X.rn = 1;

Select rows that have different values for a column, given another column

Given a table with 2 columns a, b.
I want to select rows that for a given a, I can have different values of b.
In this example, I want the first 2 lines
a | b
-----
1 | 1
1 | 2
2 | 1
2 | 1
3 | 1
Try this:
SELECT t1.*
FROM mytable AS t1
JOIN (
SELECT a
FROM mytable
GROUP BY a
HAVING COUNT(DISTINCT b) > 1
) AS t2 ON t1.a = t2.a
You didn't mention your DBMS, but in Oracle you can do:
select a,b
from (
select a,b,
count(distinct b) over (partition by a) as num_b
from the_table
) t
where num_b > 1
Unfortunately Postgres or SQL Server do not support distinct for window functions.

MS SQL Set Group ID Without Looping

I would like create a query in MS-SQL to make a column containing an incrementing group number.
This is how I want my data to return:
Column 1 | Column 2 | Column 3
------------------------------
I | 1 | 1
O | 2 | 2
O | 2 | 3
I | 3 | 4
O | 4 | 5
O | 4 | 6
O | 4 | 7
O | 4 | 8
I | 5 | 9
O | 6 | 10
Column 1 is the I and O meaning In and Out.
Column 2 is the row Group (this should increment when Column 1 changes).
Column 3 is the Row-number.
So how can I write my query so that Column 2 increments every time Column 1 changes?
Firstly, to perform this kind of operation you need some column that can identify the order of the rows. If you have a column that determines this order, an identity column for example, it can be used to do something like this:
Runnable sample:
CREATE TABLE #Groups
(
id INT IDENTITY(1, 1) , -- added identity to provide order
Column1 VARCHAR(1)
)
INSERT INTO #Groups
( Column1 )
VALUES ( 'I' ),
( 'O' ),
( 'O' ),
( 'I' ),
( 'O' ),
( 'O' ),
( 'O' ),
( 'O' ),
( 'I' ),
( 'O' );
;
WITH cte
AS ( SELECT id ,
Column1 ,
1 AS Column2
FROM #Groups
WHERE id = 1
UNION ALL
SELECT g.id ,
g.Column1 ,
CASE WHEN g.Column1 = cte.Column1 THEN cte.Column2
ELSE cte.Column2 + 1
END AS Column2
FROM #Groups g
INNER JOIN cte ON cte.id + 1 = g.id
)
SELECT *
FROM cte
OPTION (MAXRECURSION 0) -- required to allow for more than 100 recursions
DROP TABLE #Groups
This code effectively loops through the records, comparing each row to the next and incrementing the value of Column2 if the value in Column1 changes.
If you don't have an identity column, then you might consider adding one.
Credit #AeroX:
With 30K records, the last line: OPTION (MAXRECURSION 0) is required to override the default of 100 recursions when using a Common Table Expression (CTE). Setting it to 0, means that it isn't limited.
This will work if you have sqlserver 2012+
DECLARE #t table(col1 char(1), col3 int identity(1,1))
INSERT #t values
('I'), ('O'), ('O'), ('I'), ('O'), ('O'), ('O'), ('O'), ('I'), ('O')
;WITH CTE AS
(
SELECT
case when lag(col1) over (order by col3) = col1
then 0 else 1 end increase,
col1,
col3
FROM #t
)
SELECT
col1,
sum(increase) over (order by col3) col2,
col3
FROM CTE
Result:
col1 col2 col3
I 1 1
O 2 2
O 2 3
I 3 4
O 4 5
O 4 6
O 4 7
O 4 8
I 5 9
O 6 10

how to get combination of rows through query

I have a table with following fields and data.
id type value
1 1 1
2 2 1
3 2 2
4 2 3
After running the query I should get result in temp table like below
1,1
1,2
1,3
and if i have multiple value in type 1 then it should do the same
so if the data is
id type value
1 1 1
2 1 2
3 2 1
4 2 2
5 2 3
I should get result
1,1
1,2
1,3
2,1
2,2
2,3
Appreciate any help on this.
That looks like:
SELECT t1.value, t2.value
FROM table t1
JOIN table t2
ON t1.type = 1
AND t2.type = 2
;
which can also be written as:
SELECT t1.value, t2.value
FROM ( SELECT value FROM table WHERE type = 1 ) t1
CROSS
JOIN ( SELECT value FROM table WHERE type = 2 ) t2
;
(The former is simpler, but the latter makes clearer that this is semantically a CROSS JOIN, since the join doesn't actually depend on any relationship between the joined records.)
SELECT ISNULL(CAST([type] as varchar(20)), '')
+ ','
+ ISNULL(CAST([value] as varchar(20)), '')
AS CommaSeparated
FROM types