How to merge 2 tables into 1 row, with multiple entries from second table in Oracle SQL - sql

newbee question on (Oracle) SQL.
I'd like this table :
ash_id ash_contact_name ash_contact_telefoonnummber
15313 Name1 022457852114
15313 Name2 122457852114
15313 Name3 222457852114
15313 Name4 322457852114
15313 Name5 422457852114
To Look like this in 1 row :
15313 Name1 022457852114 Name2 122457852114 Name3 222457852114 Name4 322457852114 Name5 422457852114
So I get only 1x the id from first table and multiple coloms with the name with My code now looks like this :
select ash.ash_id ,
con.ash_contact_name, con.ash_contact_telefoonnummer
from D00ASH01.ash_admin_stakeholder ash,ash_contacts con
where con.ash_id = ash.ash_id and con.ash_id = 15313
order by ash.ash_id
The eventual code while not include "con.ash_id = 15313" as I will need to have all the entries. The end result will include more fields from the first table, so I can not just use the second table alone. For now, I want to start to build it up simple.
I tried to make it work with a join but did not made it.
All suggestions welcome,
thanks

Check this, it might help you. If it is not then let me know.
SELECT ash.ash_id , con.ash_contact_name, con.ash_contact_telefoonnummer FROM D00ASH01.ash_admin_stakeholder as ash INNER JOIN ash_contacts as con ON con.ash_id = ash.ash_id WHERE con.ash_id = 15313 ORDER BY ash.ash_id;

SELECT * FROM ash INNER JOIN con USING( ash_id ) Where con_id = 15313 ORDER BY ash_id ;

I'did get the solution for this :
select
c.ash_id,
c.ash_naam_kbo_NL ,
c.ash_naam_kbo_FR ,
c.ash_naam_kbo_DE,
max(Case when rn = 1 then c.ASH_CONTACT_NAME else null end) as name1,
max(Case when rn = 1 then c.ASH_CONTACT_GSMNUMMER else null end) as gsm1,
max(Case when rn = 1 then c.ASH_CONTACT_FAXNUMMER else null end) as fax1,
max(Case when rn = 1 then c.ASH_CONTACT_EMAILADRES else null end) as email1,
max(Case when rn = 2 then c.ASH_CONTACT_NAME else null end) as name2,
max(Case when rn = 2 then c.ASH_CONTACT_GSMNUMMER else null end) as gsm2,
max(Case when rn = 2 then c.ASH_CONTACT_FAXNUMMER else null end) as fax2,
max(Case when rn = 2 then c.ASH_CONTACT_EMAILADRES else null end) as email2
from (
select
table_ash.ash_id,
table_ash.ash_naam_kbo_NL ,
table_ash.ash_naam_kbo_FR ,
table_ash.ash_naam_kbo_DE,
table_contacts.ASH_CONTACT_NAME,
table_contacts.ASH_CONTACT_GSMNUMMER,
table_contacts.ASH_CONTACT_FAXNUMMER,
table_contacts.ASH_CONTACT_EMAILADRES,
ROW_NUMBER () over (partition by table_ash.ash_id order by table_ash.ash_id,
table_ash.ash_naam_kbo_NL) rn
from
ASH_ADMIN_STAKEHOLDER table_ash,
ash_contacts table_contacts
where
table_ash.ash_id = table_contacts.ash_id) c
group by
c.ash_id,
c.ash_naam_kbo_NL ,
c.ash_naam_kbo_FR ,
c.ash_naam_kbo_DE;
So, there's a "select" from a "select". The trick is to generate a rownnumber and use it like a index. For as many as rownumbers as needed, include a max function in the query. This code is the base of answer I needed.

SQL code written in Oracle:
WITH CTE AS(
SELECT
            UP.CLASS,
            UP.NS || UP.RN AS NSR,
            UP.VAL
FROM
            (
            SELECT
                        ROW_NUMBER ()
         OVER (
           PARTITION BY S.CLASS
            ORDER BY
                        S.CLASS) RN,
                        S.*
            FROM
                        STAKEHOLDER S
            ORDER BY
                        CLASS,
                        SID) SS
UNPIVOT (VAL FOR NS IN (NAME, SID)) UP
)
SELECT
            *
FROM
            CTE
PIVOT(MAX(VAL) FOR NSR IN ('NAME1' AS NAME1,
            'SID1' AS SID1,
            'NAME2' AS NAME2,
            'SID2' AS SID2,
            'NAME3' AS NAME3,
            'SID3' AS SID3))
This is not difficult if we handle it with our natural way of thinking. After grouping the table by CLASS, we convert NAME and SID columns into rows and create names commanding values to be converted to columns. Format of names is the original column name + number of subgroups, like NAME1, SID1, NAME2, SID2,… for group 1 and NAME1, SID1, … for group2. Then we concatenate groups and transpose row to columns. The problem is SQL does not support dynamic row-to-column/column-to-row transposition. When the number of columns is small and columns are fixed, the language can mange to do the transpositions. As the number of columns increases, the scenario becomes more and more awkward. Enumerating all columns to be converted is complicated and SQL code becomes bloated. If columns are dynamic, SQL needs to turn to complex and roundabout ways to handle them.
Yet, it is really easy to code the transposition task with the open-source esProc SPL:
| |A|
|1|=connect("ORACLE")|
|2|=A1.query#x("SELECT \* FROM STAKEHOLDER ORDER BY CLASS,SID")|
|3|=A2.fname().m(2:)|
|4|=A2.group#o(CLASS)|
|5|=A4.conj(\~.news(A3;CLASS,A3(#)/A4.\~.#:COL,\~:VAL))|

Related

flatten data in SQL based on fixed set of column

I am stuck with a specific scenario of flattening the data and need help for it. I need the output as flattened data where the column values are not fixed. Due to this I want to restrict the output to fixed set of columns.
Given Table 'test_table'
ID
Name
Property
1
C1
xxx
2
C2
xyz
2
C3
zz
The scenario is, column Name can have any no. of values corresponding to an ID. I need to flatten the data based in such a way that there is one row per ID field. Since the Name field varies with each ID, I want to flatten it for fix 3 columns like Co1, Co2, Co3. The output should look like
ID
Co1
Co1_Property
Co2
Co2_Property
Co3
Co3_Property
1
C1
xxx
null
null
2
C2
xyz
C3
zz
Could not think of a solution using Pivot or aggregation. Any help would be appreciated.
You can use arrays:
select id,
array_agg(name order by name)[safe_ordinal(1)] as name_1,
array_agg(property order by name)[safe_ordinal(1)] as property_1,
array_agg(name order by name)[safe_ordinal(2)] as name_2,
array_agg(property order by name)[safe_ordinal(2)] as property_2,
array_agg(name order by name)[safe_ordinal(3)] as name_3,
array_agg(property order by name)[safe_ordinal(3)] as property_3
from t
group by id;
All current answers are too verbose and involve heavy repetition of same fragments of code again and again and if you need to account more columns you need to copy paste and add more lines which will make it even more verbose!
My preference is to avoid such type of coding and rather use something more generic as in below example
select * from (
select *, row_number() over(partition by id) col
from `project.dataset.table`)
pivot (max(name) as name, max(property) as property for col in (1, 2, 3))
If applied to sample data in your question - output is
If you want to change number of output columns - you just simply modify for col in (1, 2, 3) part of query.
For example if you would wanted to have 5 columns - you would use for col in (1, 2, 3, 4, 5) - that simple!!!
The standard practice is to use conditional aggregation. That is, to use CASE expressions to pick which row goes to which column, then MAX() to collapse multiple rows into individual rows...
SELECT
id,
MAX(CASE WHEN name = 'C1' THEN name END) AS co1,
MAX(CASE WHEN name = 'C1' THEN property END) AS co1_property,
MAX(CASE WHEN name = 'C2' THEN name END) AS co2,
MAX(CASE WHEN name = 'C2' THEN property END) AS co2_property,
MAX(CASE WHEN name = 'C3' THEN name END) AS co3,
MAX(CASE WHEN name = 'C3' THEN property END) AS co3_property
FROM
yourTable
GROUP BY
id
Background info:
Not having an ELSE in the CASE expression implicitly means ELSE NULL
The intention is therefore for each column to recieve NULL from every input row, except for the row being pivoted into that column
Aggregates, such as MAX() essentially skip NULL values
MAX( {NULL,NULL,'xxx',NULL,NULL} ) therefore equals 'xxx'
A similar approach "bunches" the values to the left (so that NULL values always only appears to the right...)
That approach first uses row_number() to give each row a value corresponding to which column you want to put that row in to..
WITH
sorted AS
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY name) AS seq_num
FROM
yourTable
)
SELECT
id,
MAX(CASE WHEN seq_num = 1 THEN name END) AS co1,
MAX(CASE WHEN seq_num = 1 THEN property END) AS co1_property,
MAX(CASE WHEN seq_num = 2 THEN name END) AS co2,
MAX(CASE WHEN seq_num = 2 THEN property END) AS co2_property,
MAX(CASE WHEN seq_num = 3 THEN name END) AS co3,
MAX(CASE WHEN seq_num = 3 THEN property END) AS co3_property
FROM
yourTable
GROUP BY
id

Pivoting rows to columns with custom column names in SQL Server

I'm having some difficulty with pivoting rows into columns as I also want to name the columns. Here is my current code, modified:
SELECT Message, value
FROM Table1
CROSS APPLY
(
SELECT value FROM STRING_SPLIT(Message,'"')
WHERE value LIKE '%.%'
)
AS SourceTable
And my current output:
Message value
------------ -----
longmessage1 hello
longmessage1 hi
longmessage1 hey
longmessage1 hola
Just for the sake of shortness, I replaced the actual Message with longmessage1 above. My desired output:
Message greeting1 greeting2 greeting3 greeting4
------------ --------- --------- --------- ---------
longmessage1 hello hi hey hola
The maximum amount of greetings is six, and if a Message doesn't have six, I'm fine with the value of, say greeting 4 and 5 to be NULL.
FYI- I am using SQL Server. I think I could somehow use PIVOT to do this but I'm stuck on the custom column name part and if CROSS APPLY was even the right idea. If anyone could offer some suggestions, that'd be terrific. Thank you!
You can use row_number() and conditional aggregation:
SELECT t1.Message, a.*
FROM Table1 t1 CROSS APPLY
(SELECT MAX(CASE WHEN seqnum = 1 THEN value END) as greeting1,
MAX(CASE WHEN seqnum = 2 THEN value END) as greeting2,
MAX(CASE WHEN seqnum = 3 THEN value END) as greeting3,
MAX(CASE WHEN seqnum = 4 THEN value END) as greeting4,
MAX(CASE WHEN seqnum = 5 THEN value END) as greeting5,
MAX(CASE WHEN seqnum = 6 THEN value END) as greeting6
FROM (SELECT s.value,
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as seqnum
FROM STRING_SPLIT(t1.Message,'"')
WHERE value LIKE '%.%'
) s
) s;
Note: In practice, this will probably preserve the ordering of the values. However that is not guaranteed -- based on the documentation.

query case when postgresql

I have query like this :
select
case when code=31 then name end as name1,
case when code=32 then name end as name2
from master where code=31 or partner_id=32
and the result just like this :
I want to make just 1 row but 2 column which is the value is like the table above.
Anyone can help me?
Thanks
Use aggregation:
select max(case when code=31 then name end) as name1,
max(case when code=32 then name end) as name2
from master
where code = 31 or partner_id = 32;

pivot table returns more than 1 row for the same ID

I have a sql code which I am using to do pivot. Code is as follows:
SELECT DISTINCT PersonID
,MAX(pivotColumn1)
,MAX(pivotColumn2) --originally these were in 2 separate rows)
FROM(SELECT srcID, PersonID, detailCode, detailValue) FROM src) AS SrcTbl
PIVOT(MAX(detailValue) FOR detailCode IN ([pivotColumn1],[pivotColumn2])) pvt
GROUP BY PersonID
In the source data the ID has 2 separate rows due to having its own ID which separates the values. I have now pivoted it and its still giving me 2 separate rows for the ID even though i grouped it and used aggregation on the pivot columns. Ay idea whats wrong with the code?
So I have all my possible detailCode listed in the IN clause. So I have null returned when the value is none but I want it all summarised in 1 row. See image below.
If those are all the options of detailCode , you can use conditional aggregation with CASE EXPRESSION instead of Pivot:
SELECT t.personID,
MAX(CASE WHEN t.detailCode = 'cas' then t.detailValue END) as cas,
MAX(CASE WHEN t.detailCode = 'buy' then t.detailValue END) as buy,
MAX(CASE WHEN t.detailCode = 'sel' then t.detailValue END) as sel,
MAX(CASE WHEN t.detailCode = 'pla' then t.detailValue END) as pla
FROM YourTable t
GROUP BY t.personID

SQL using CASE in SELECT with GROUP BY. Need CASE-value but get row-value

so basicially there is 1 question and 1 problem:
1. question - when I have like 100 columns in a table(and no key or uindex is set) and I want to join or subselect that table with itself, do I really have to write out every column name?
2. problem - the example below shows the 1. question and my actual SQL-statement problem
Example:
A.FIELD1,
(SELECT CASE WHEN B.FIELD2 = 1 THEN B.FIELD3 ELSE null FROM TABLE B WHERE A.* = B.*) AS CASEFIELD1
(SELECT CASE WHEN B.FIELD2 = 2 THEN B.FIELD4 ELSE null FROM TABLE B WHERE A.* = B.*) AS CASEFIELD2
FROM TABLE A
GROUP BY A.FIELD1
The story is: if I don't put the CASE into its own select statement then I have to put the actual rowname into the GROUP BY and the GROUP BY doesn't group the NULL-value from the CASE but the actual value from the row. And because of that I would have to either join or subselect with all columns, since there is no key and no uindex, or somehow find another solution.
DBServer is DB2.
So now to describing it just with words and no SQL:
I have "order items" which can be divided into "ZD" and "EK" (1 = ZD, 2 = EK) and can be grouped by "distributor". Even though "order items" can have one of two different "departements"(ZD, EK), the fields/rows for "ZD" and "EK" are always both filled. I need the grouping to consider the "departement" and only if the designated "departement" (ZD or EK) is changing, then I want a new group to be created.
SELECT
(CASE WHEN TABLE.DEPARTEMENT = 1 THEN TABLE.ZD ELSE null END) AS ZD,
(CASE WHEN TABLE.DEPARTEMENT = 2 THEN TABLE.EK ELSE null END) AS EK,
TABLE.DISTRIBUTOR,
sum(TABLE.SOMETHING) AS SOMETHING,
FROM TABLE
GROUP BY
ZD
EK
TABLE.DISTRIBUTOR
TABLE.DEPARTEMENT
This here worked in the SELECT and ZD, EK in the GROUP BY. Only problem was, even if EK was not the designated DEPARTEMENT, it still opened a new group if it changed, because he was using the real EK value and not the NULL from the CASE, as I was already explaining up top.
And here ladies and gentleman is the solution to the problem:
SELECT
(CASE WHEN TABLE.DEPARTEMENT = 1 THEN TABLE.ZD ELSE null END) AS ZD,
(CASE WHEN TABLE.DEPARTEMENT = 2 THEN TABLE.EK ELSE null END) AS EK,
TABLE.DISTRIBUTOR,
sum(TABLE.SOMETHING) AS SOMETHING,
FROM TABLE
GROUP BY
(CASE WHEN TABLE.DEPARTEMENT = 1 THEN TABLE.ZD ELSE null END),
(CASE WHEN TABLE.DEPARTEMENT = 2 THEN TABLE.EK ELSE null END),
TABLE.DISTRIBUTOR,
TABLE.DEPARTEMENT
#t-clausen.dk: Thank you!
#others: ...
Actually there is a wildcard equality test.
I am not sure why you would group by field1, that would seem impossible in your example. I tried to fit it into your question:
SELECT FIELD1,
CASE WHEN FIELD2 = 1 THEN FIELD3 END AS CASEFIELD1,
CASE WHEN FIELD2 = 2 THEN FIELD4 END AS CASEFIELD2
FROM
(
SELECT * FROM A
INTERSECT
SELECT * FROM B
) C
UNION -- results in a distinct
SELECT
A.FIELD1,
null,
null
FROM
(
SELECT * FROM A
EXCEPT
SELECT * FROM B
) C
This will fail for datatypes that are not comparable
No, there's no wildcard equality test. You'd have to list every field you want tested individually. If you don't want to test each individual field, you could use a hack such as concatenating all the fields, e.g.
WHERE (a.foo + a.bar + a.baz) = (b.foo + b.bar + b.az)
but either way, you're listing all of the fields.
I might tend to solve it something like this
WITH q as
(SELECT
Department
, (CASE WHEN DEPARTEMENT = 1 THEN ZD
WHEN DEPARTEMENT = 2 THEN EK
ELSE null
END) AS GRP
, DISTRIBUTOR
, SOMETHING
FROM mytable
)
SELECT
Department
, Grp
, Distributor
, sum(SOMETHING) AS SumTHING
FROM q
GROUP BY
DEPARTEMENT
, GRP
, DISTRIBUTOR
If you need to find all rows in TableA that match in TableB, how about INTERSECT or INTERSECT DISTINCT?
select * from A
INTERSECT DISTINCT
select * from B
However, if you only want rows from A where the entire row matches the values in a row from B, then why does your sample code take some values from A and others from B? If the row matches on all columns, then that would seem pointless. (Perhaps your question could be explained a bit more fully?)