SQL combine rows based on group by in view - sql

I have a table:
c1 c2
----------
A B
A C
X P
X Q
How can i create a view that shows this as
c1 c2 c3
---------------
A B C
X P Q

You can use aggregation:
select c1, min(c2) as c2, max(c2) as c3
from t
group by c1;

You can self join the table:
select
t1.c1, t1.c2, t2.c2 as c3
from tablename t1 inner join tablename t2
on t1.c1 = t2.c1 and t1.c2 < t2.c2
See the demo.
Results:
| c1 | c2 | c3 |
| --- | --- | --- |
| A | B | C |
| X | P | Q |

Related

count on one column and group by another column

Suppose that we have a sample table
c1 | c2
--------------
a | b
a | b
a | c
a | c
a | c
d | e
d | e
How can we turn this table into the following format
c1 | c2 | c3
--------------------------
a | b | 2
a | c | 3
d | e | 2
where c3 contains the count of c2 based on distinct c1 value.
This is a group by with two keys:
select c1, c2, count(*)
from t
group by c1, c2;
select c1, c2, count(*) as c3
from your_table
group by c1, c2

Join on two tables gives duplicate results

i have a table with data that I want to join unto another table. Problem is that the join can happen on two columns of the same table, where I want to get the first join to work and if this Fails i want the second join to give me a valid result.
Base table:
| ID1 | ID2 | Value |
| a1 | a2 | val_1 |
| b1 | b2 | val_2 |
| c1 | c2 | val_3 |
join Table:
| ID1 | ID2 | Join_Value |
| | a2 | join_val_1 |
| b1 | | join_val_2 |
| c1 | c2 | join_val_3 |
What i tried was this:
select base.id1, base.id2, Value, isnull(j1.Join_value,j2.Join_value) Join_Value from base
left join Join j1 on j1.id1 = base.id1
left join Join j2 on j2.id2 = base.id2
The Result is this:
| ID1 | ID2 | Value | Join_Value |
| a1 | a2 | val_1 | join_val_1 |
| b1 | b2 | val_2 | join_val_2 |
| c1 | c2 | val_3 | join_val_3 |
| c1 | c2 | val_3 | join_val_3 |
What i want is this:
| ID1 | ID2 | Value | Join_Value |
| a1 | a2 | val_1 | join_val_1 |
| b1 | b2 | val_2 | join_val_2 |
| c1 | c2 | val_3 | join_val_3 |
I hope i made my Problem clear.
You don't need to join the same table twice. Just specify the condition in the ON
select b.ID1, b.ID2, b.[Value], j.Join_Value
from [base] b
inner join [join] j on b.ID1 = j.ID1
or (
j.ID1 = ''
and b.ID2 = j.ID2
)
You are going to get duplicate rows for for the c1 and c2 rows because they match on both of your Join table joins (j1 and j2).
A quick fix is to add a DISTINCT to your query:
select DISTINCT base.id1, base.id2, Value, isnull(j1.Join_value,j2.Join_value) Join_Value
from base
left join Join j1 on j1.id1 = base.id1
left join Join j2 on j2.id2 = base.id2
A better fix, depending on your DBMS is to use a window function:
select id1, id2, Value, Join_Value
FROM (
select base.id1, base.id2, Value, isnull(j1.Join_value,j2.Join_value) Join_Value,
ROW_NUMBER() OVER(
PARTITION BY base.id1, base.id2 -- Group rows based on (id1, id2) combination
ORDER BY j1.id1 -- If more than one row, give priority to row with "id1" value
) AS RowNum
from base
left join Join j1 on j1.id1 = base.id1
left join Join j2 on j2.id2 = base.id2
) src
WHERE RowNum = 1 -- Only return one row
This will make sure you always one row maximum per (id1, id2) combination.
Try:
select *
from base b
join [join] j on b.id1 = j.id1 or b.id2 = j.id2
First, your version does exactly what you want. Here is a db<>fiddle.
Second, for more control over the matching, you can use a lateral join. This allows you to choose only one matching row -- say the one where both ids match:
select b.id1, b.id2, b.value, jt.join_value
from base b cross apply
(select top (1) jt.*
from jointable jt
where b.id1 = jt.id1 or
b.id2 = jt.id2
order by (case when b.id1 = jt.id1 then 1 else 0 end) +
(case when b.id2 = jt.id2 then 1 else 0 end) desc
) jt ;

SQL Join 2 tables without a relation

Let's say I have the following tables:
Table1 with cols: A, B
Table2 with col: C
And I have a variable #d.
|Table1| |Table2| #d = 5;
------ ------
|A | B| | C |
------- -------
a1 | b1 c1
a2 | b2 c2
How can I display following output?
| ResultTable |
------------------
|A | B | C | d|
a1 b1 c1 5
a2 b2 c2 5
PS: I am using T-SQL.
You can use row_number to give each row a number and join on that. This assumes both tables have the same # of rows.
select *, #d from (
select *, row_number() over (order by A) rn
from Table1
) t1 join (
select *, row_number() over (order by C) rn
from Table2
) t2 on t1.rn = t2.rn

Sql Server get first matching value

I have two tables History and Historyvalues:
History
HID(uniqeidentifier) | Version(int)
a1 | 1
a2 | 2
a3 | 3
a4 | 4
Historyvalues
HVID(uniqeidentifier) | HID(uniqeidentifier) | ControlID(uniqeidentifier) | Value(string)
b1 | a1 | c1 | value1
b2 | a2 | c1 | value2
b3 | a2 | c2 | value3
Now I Need a query where I can get a list with the last historyvalue of each control from a specific Version like:
Get the last values from Version 3 -> receiving ->
HVID | ControlID | Value
b2 | c1 | value2
b3 | c2 | value3
I tried something like this:
Select HVID, ControlId, max(Version), Value from
(
Select HVID, ControlId, Version, Value
from History inner JOIN
Historyvalues ON History.HID = Historyvalues.HID
where Version <= 3
) as a
group by ControlId
order by Version desc
but this does not work.
Are there any ideas?
Thank you very much for your help.
Best regards
Latest version from each control with your specific Version (WHERE t1.Version <= 3)
Query:
SQLFIDDLEExample
SELECT HVID, ControlId, Version, Value
FROM
(
SELECT t2.HVID, t2.ControlId, t1.Version, t2.Value,
ROW_NUMBER() OVER(PARTITION BY t2.ControlId ORDER BY t1.Version DESC) as rnk
FROM History t1
JOIN Historyvalues t2
ON t1.HID = t2.HID
WHERE t1.Version <= 3
) AS a
WHERE a.rnk = 1
ORDER BY a.Version desc
Result:
| HVID | CONTROLID | VERSION | VALUE |
|------|-----------|---------|--------|
| b2 | c1 | 2 | value2 |
| b3 | c2 | 2 | value3 |
here is your solution
Select Historyvalues.HVID,Historyvalues.ControlID,Historyvalues.Value
from Historyvalues
inner join History on Historyvalues.hid=History.hid
where Historyvalues.hvid in (
select MAX(Historyvalues.hvid) from Historyvalues
inner join History on Historyvalues.hid=History.hid
group by ControlID)

Union of SQL tables, but only taking the first instance of an ID

I have three tables with identical columns that I need to union together into one table. Some IDs appear in more than one table, and I need to use the ID from the lowest numbered table first, and ignore that ID in any subsequent tables. Note that only the ID is the same, there aren't any duplicate rows between the tables.
So as an example, these three tables need to be transformed to the final table:
**Table 1**
+----+----+----+
| ID | C1 | C2 |
+----+----+----+
| 01 | AA | BB |
| 02 | CC | DD |
| 03 | EE | FF |
+----+----+----+
**Table 2**
+----+----+----+
| ID | C1 | C2 |
+----+----+----+
| 03 | GG | HH |
| 04 | II | JJ |
| 05 | KK | LL |
+----+----+----+
**Table 3**
+----+----+----+
| ID | C1 | C2 |
+----+----+----+
| 01 | MM | NN |
| 04 | OO | PP |
| 06 | QQ | RR |
+----+----+----+
**Resulting Table**
+----+----+----+
| ID | C1 | C2 |
+----+----+----+
| 01 | AA | BB |
| 02 | CC | DD |
| 03 | EE | FF |
| 04 | II | JJ |
| 05 | KK | LL |
| 06 | QQ | RR |
+----+----+----+
I have a vague idea of a solution using left joins and 'IS NULL' conditions, but that could get complicated very quickly, especially if the number of tables increases.
Is there a way of specifying duplicates only on one column, not on a whole row?
Since you are using Access, you can use FIRST function, and I think you only need this:
SELECT ID, First(C1) As C1, First(C2) as C2
FROM (
SELECT * FROM Table1 UNION SELECT * FROM Table2 UNION SELECT * FROM Table3 )
GROUP BY ID
If NULL values don't count, this logic can be applied:
get all IDs, that exist in the 3 tables (this is the union part)
left join all the tables, and using COALESCE, find the first non-null value for each column
In Access MS SQL:
select ids.ID, COALESCE(t1.C1,t2.C1,t3.C1) C1, COLAESCE(t1.C2,t2.C2,t3.C2) C2
from
(SELECT ID FROM Table1
UNION
SELECT ID FROM Table2
UNION
SELECT ID FROM Table3) AS ids
left join Table1 t1 on ids.id=t1.id
left join Table2 t2 on ids.id=t2.id
left join Table3 t3 on ids.id=t3.id
But Access sadly doesn't support COALESCE... It has NZ:
select ids.ID, NZ(NZ(t1.C1,t2.C1),t3.C1) C1, NZ(NZ(t1.C2,t2.C2),t3.C2) C2
from
(SELECT ID FROM Table1
UNION
SELECT ID FROM Table2
UNION
SELECT ID FROM Table3) AS ids
left join Table1 t1 on ids.id=t1.id
left join Table2 t2 on ids.id=t2.id
left join Table3 t3 on ids.id=t3.id
(Though I can't test it not having a MS Access at hand at the moment)
Side note: in Oracle, you could do this (I didn't want to delete it):
select ids.ID, NVL(NVL(t1.C1,t2.C1),t3.C1) C1, NVL(NVL(t1.C2,t2.C2),t3.C2) from
(SELECT ID FROM Table1
UNION
SELECT ID FROM Table2
UNION
SELECT ID FROM Table3) ids
left join Table1 t1 on ids.id=t1.id
left join Table2 t2 on ids.id=t2.id
left join Table3 t3 on ids.id=t3.id
If however NULL values are considered valid, that is a bit of a problem, as MS Access doesn't support CASE-WHEN statements - unless in VBA...
In Oracle, this would do:
select ids.ID,
CASE WHEN t1.ID IS NOT NULL THEN t1.C1
WHEN t1.ID IS NULL AND t2.ID IS NOT NULL THEN t2.C1
ELSE t3.C1 END C1,
CASE WHEN t1.ID IS NOT NULL THEN t1.C2
WHEN t1.ID IS NULL AND t2.ID IS NOT NULL THEN t2.C2
ELSE t3.C2 END C2
from
(SELECT ID FROM Table1
UNION
SELECT ID FROM Table2
UNION
SELECT ID FROM Table3) AS ids
left join Table1 t1 on ids.id=t1.id
left join Table2 t2 on ids.id=t2.id
left join Table3 t3 on ids.id=t3.id