Inserting set of rows for every ID in another table - sql

this is an initial table (this is just a part of a larger table where Article ID's can vary), database is MS Sql.
-----------------------------------
|ArticleID | GroupID |
-----------------------------------
| 1 | NULL |
-----------------------------------
| 2 | NULL |
-----------------------------------
| 3 | NULL |
-----------------------------------
| 4 | NULL |
-----------------------------------
Set of rows that should be entered for each ArticleID looks something like this:
------------------------
| GroupID |
------------------------
| A |
------------------------
| B |
------------------------
| C |
------------------------
| D |
------------------------
Result table should look something like this:
-----------------------------------
|ArticleID | GroupID |
-----------------------------------
| 1 | NULL |
-----------------------------------
| 1 | A |
-----------------------------------
| 1 | B |
-----------------------------------
| 1 | C |
-----------------------------------
| 1 | D |
-----------------------------------
| 2 | NULL |
-----------------------------------
| 2 | A |
-----------------------------------
| 2 | B |
-----------------------------------
| 2 | C |
-----------------------------------
| 2 | D |
-----------------------------------
| 3 | NULL |
-----------------------------------
| 3 | A |
-----------------------------------
| 3 | B |
-----------------------------------
| 3 | C |
-----------------------------------
| 3 | D |
-----------------------------------
| 4 | NULL |
-----------------------------------
| 4 | A |
-----------------------------------
| 4 | B |
-----------------------------------
| 4 | C |
-----------------------------------
| 4 | D |
-----------------------------------
Any suggestion how to insert it efficiently?
Thanks a lot for you suggestion.
Regards

This is a cross join between two sets.
with a as (
select * from(values (1),(2),(3),(4))v(ArticleId)
), g as (
select * from(values (null),('A'),('B'),('C'),('D'))v(GroupId)
)
select *
from a cross join g;
To insert into the original table you could do:
with g as (select * from(values('A'),('B'),('C'),('D'))v(GroupId))
insert into t
select t.ArticleId, g.GroupId
from t cross join g;
See Example Fiddle

Related

SQL Query - Add column data from another table adding nulls

I have 2 tables, tableStock and tableParts:
tableStock
+----+----------+-------------+
| ID | Num_Part | Description |
+----+----------+-------------+
| 1 | sr37 | plate |
+----+----------+-------------+
| 2 | sr56 | punch |
+----+----------+-------------+
| 3 | sl30 | crimper |
+----+----------+-------------+
| 4 | mp11 | holder |
+----+----------+-------------+
tableParts
+----+----------+-------+
| ID | Location | Stock |
+----+----------+-------+
| 1 | A | 2 |
+----+----------+-------+
| 3 | B | 5 |
+----+----------+-------+
| 5 | C | 2 |
+----+----------+-------+
| 7 | A | 1 |
+----+----------+-------+
And I just want to do this:
+----+----------+-------------+----------+-------+
| ID | Num_Part | Description | Location | Stock |
+----+----------+-------------+----------+-------+
| 1 | sr37 | plate | A | 2 |
+----+----------+-------------+----------+-------+
| 2 | sr56 | punch | NULL | NULL |
+----+----------+-------------+----------+-------+
| 3 | sl30 | crimper | B | 5 |
+----+----------+-------------+----------+-------+
| 4 | mp11 | holder | NULL | NULL |
+----+----------+-------------+----------+-------+
List ALL the rows of the first table and if the second table has the info, in this case 'location' and 'stock', add to the column, if not, just null.
I have been using inner and left join but some rows of the first table disappear because the lack of data in the second one:
select tableStock.ID, tableStock.Num_Part, tableStock.Description, tableParts.Location, tableParts.Stock from tableStock inner join tableParts on tableStock.ID = tableParts.ID;
What can I do?
You can use left join. Here is the demo.
select
s.ID,
Num_Part,
Description,
Location,
Stock
from Stock s
left join Parts p
on s.ID = p.ID
order by
s.ID
output:
| id | num_part | description | location | stock |
| --- | -------- | ----------- | -------- | ----- |
| 1 | sr37 | plate | A | 2 |
| 2 | sr56 | punch | NULL | NULL |
| 3 | sl30 | crimper | B | 5 |
| 4 | mp11 | holder | NULL | NULL |

How to handle multiple rows fulfilling criteria when joining Oracle tables

Given a table of roles, companies and a employee table where we store for each employee which role he/she has at each company.
I'm trying to create a view which indicates for each combination of role and company and employee by a ‘Y’ or ‘N’ in the “checked_yn” column, whether this employee has this role at this company.
company table
----------------
|ID | name |
-----------------
| 1 | A |
| 2 | B |
-----------------
roles table
-------------
|ID | role |
-------------
| 1 | X |
| 2 | Y |
| 3 | Z |
-------------
employee table
----------------------------------------------
|ID | company_id | role_id | employee_log_id |
---------------------------------------------|
| 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 1 |
| 3 | 2 | null | 1 |
----------------------------------------------
The desired outcome is this:
EMPLOYEE_ROLES_VW view
------------------------------------------------------------------------
|Id |company_id | role_id | Checked_yn | employee_id | employee_log_id |
|----------------------------------------------------------------------|
| 1 | 1 | 1 | Y | 1 | 1 |
| 2 | 1 | 2 | Y | 2 | 1 |
| 3 | 1 | 3 | N | null | 1 |
| 4 | 2 | 1 | N | null | 1 |
| 5 | 2 | 2 | N | null | 1 |
| 6 | 2 | 3 | N | null | 1 |
------------------------------------------------------------------------
This is my current query:
with ROLES_X_COMP as (SELECT ROL.ID AS X_ROLE_ID,
COM.ID AS X_COMPANY_ID,
FROM ROLES ROL
CROSS JOIN COMPANY COM)
SELECT ROWNUM AS ID,
EMP.ID AS SMCR_EMPLOYEE_ID,
EMP.EMPLOYEE_LOG_ID AS EMPLOYEE_LOG_ID,
ROLES_X_COMP.X_ROLE_ID ,
EMP.ROLE_ID AS ROLE_ID,
ROLES_X_COMP.X_COMPANY_ID,
EMP.COMPANY_ID AS COMPANY_ID,
CASE
WHEN ROLES_X_COMP.X_ROLE_ID = SE.ROLE_ID AND ROLES_X_COMP.X_COMPANY_ID =
SE.COMPANY_ID THEN 'Y'
ELSE 'N' END AS CHECKED_YN
FROM ROLES_X_COMP
LEFT OUTER JOIN EMPLOYEE EMP ON ROLES_X_COMP.X_COMPANY_ID = EMP.COMPANY_ID
Because of the join on EMPLOYEE “finds” the company with id=1 twice it joins twice with the cross join of role and company table. So I'm getting this result:
------------------------------------------------------------------------
|Id |company_id | role_id | Checked_yn | employee_id | employee_log_id |
|----------------------------------------------------------------------|
| 1 | 1 | 1 | Y | 1 | 1 |
| 2 | 1 | 2 | N | 1 | 1 |
| 3 | 1 | 3 | N | 1 | 1 |
| 4 | 1 | 1 | N | 2 | 1 |
| 5 | 1 | 2 | Y | 2 | 1 |
| 6 | 1 | 3 | N | 2 | 1 |
| 7 | 2 | 1 | N | 3 | 1 |
| 8 | 2 | 2 | N | 3 | 1 |
| 9 | 2 | 3 | N | 3 | 1 |
------------------------------------------------------------------------
I think a JOIN might be the wrong option here and a UNION more appropriate but I can't figure it out.
Use a partitioned outer join:
Query:
SELECT ROWNUM AS id,
e.company_id,
r.id AS role_id,
NVL2( e.role_id, 'Y', 'N' ) AS CheckedYN,
e.role_id AS employee_id,
e.employee_log_id
FROM roles r
LEFT OUTER JOIN
employee e
PARTITION BY ( e.company_id, e.employee_log_id )
ON ( r.id = e.role_id )
or (depending on how you want to partition and join the data):
SELECT ROWNUM AS id,
c.id AS company_id,
r.id AS role_id,
NVL2( e.role_id, 'Y', 'N' ) AS CheckedYN,
e.role_id AS employee_id,
e.employee_log_id
FROM roles r
CROSS JOIN
company c
LEFT OUTER JOIN
employee e
PARTITION BY ( e.employee_log_id )
ON ( c.id = e.company_id AND r.id = e.role_id )
Output:
Both output the same for the test data but may give differing results depending on your actual data.
ID | COMPANY_ID | ROLE_ID | CHECKEDYN | EMPLOYEE_ID | EMPLOYEE_LOG_ID
-: | ---------: | ------: | :-------- | ----------: | --------------:
1 | 1 | 1 | Y | 1 | 1
2 | 1 | 2 | Y | 2 | 1
3 | 1 | 3 | N | null | 1
4 | 2 | 1 | N | null | 1
5 | 2 | 2 | N | null | 1
6 | 2 | 3 | N | null | 1
db<>fiddle here
AND ROLES_X_COMP.X_ROLE_ID = EMP.ROLE_ID
Is missing at the end of your query
But the outcome will be
EMPLOYEE_ROLES_VW view
------------------------------------------------------------------------
|Id |company_id | role_id | Checked_yn | employee_id | employee_log_id |
|----------------------------------------------------------------------|
| 1 | 1 | 1 | Y | 1 | 1 |
| 2 | 1 | 2 | Y | 2 | 1 |
| 3 | 1 | 3 | N | null | null |
| 4 | 2 | 1 | N | null | null |
| 5 | 2 | 2 | N | null | null |
| 6 | 2 | 3 | N | null | null |
------------------------------------------------------------------------

Merge columns on two left joins

I have 3 tables as shown:
Video
+----+--------+-----------+
| id | name | videoSize |
+----+--------+-----------+
| 1 | video1 | 1MB |
| 2 | video2 | 2MB |
| 3 | video3 | 3MB |
+----+--------+-----------+
Survey
+----+---------+-----------+
| id | name | questions |
+----+---------+-----------+
| 1 | survey1 | 1 |
| 2 | survey2 | 2 |
| 3 | survey3 | 3 |
+----+---------+-----------+
Sequence
+----+---------+-----------+----------+
| id | videoId | surveyId | sequence |
+----+---------+-----------+----------+
| 1 | null | 1 | 1 |
| 2 | 2 | null | 2 |
| 3 | null | 3 | 3 |
+----+---------+-----------+----------+
I would like to query Sequence and join on both of video and survey tables and merge common columns without specifying the column names (in this case name) like this:
Query Result:
+----+---------+-----------+----------+---------+-----------+-----------+
| id | videoId | surveyId | sequence | name | videoSize | questions |
+----+---------+-----------+----------+---------+-----------+-----------+
| 1 | null | 1 | 1 | survey1 | null | 1 |
| 2 | 2 | null | 2 | video2 | 2MB | null |
| 3 | null | 3 | 3 | survey3 | null | 3 |
+----+---------+-----------+----------+---------+-----------+-----------+
Is this possible?
BTW the below sql doesn't work as it doesn't merge on the name field:
SELECT * FROM "Sequence"
LEFT JOIN "Survey" ON "Survey"."id" = "Sequence"."surveyId"
LEFT JOIN "Video" ON "Video"."id" = "Sequence"."videoId"
This query will show what you want:
select
s.*,
coalesce(y.name, v.name) as name, -- picks the right column
v.videoSize,
y.questions
from sequence s
left join survey y on y.id = s.surveyId
left join video v on v.id = s.videoId
However, the SQL standard requires you to name the columns you want. The only exception being * as shown above.

SQL compare multiple rows or partitions to find matches

The database I'm working on is DB2 and I have a problem similar to the following scenario:
Table Structure
-------------------------------
| Teacher Seating Arrangement |
-------------------------------
| PK | seat_argmt_id |
| | teacher_id |
-------------------------------
-----------------------------
| Seating Arrangement |
-----------------------------
|PK FK | seat_argmt_id |
|PK | Row_num |
|PK | seat_num |
|PK | child_name |
-----------------------------
Table Data
------------------------------
| Teacher Seating Arrangement|
------------------------------
| seat_argmt_id | teacher_id |
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 2 |
------------------------------
---------------------------------------------------
| Seating Arrangement |
---------------------------------------------------
| seat_argmt_id | row_num | seat_num | child_name |
| 1 | 1 | 1 | Abe |
| 1 | 1 | 2 | Bob |
| 1 | 1 | 3 | Cat |
| | | | |
| 2 | 1 | 1 | Abe |
| 2 | 1 | 2 | Bob |
| 2 | 1 | 3 | Cat |
| | | | |
| 3 | 1 | 1 | Abe |
| 3 | 1 | 2 | Cat |
| 3 | 1 | 3 | Bob |
| | | | |
| 4 | 1 | 1 | Abe |
| 4 | 1 | 2 | Bob |
| 4 | 1 | 3 | Cat |
| 4 | 2 | 2 | Dan |
---------------------------------------------------
I want to see where there are duplicate seating arrangements for a teacher. And by duplicates I mean where the row_num, seat_num, and child_name are the same among different seat_argmt_id for one teacher_id. So with the data provided above, only seat id 1 and 2 are what I would want to pull back, as they are duplicates on everything but the seat id. If all the children on the 2nd table are exact (sans the primary & foreign key, which is seat_argmt_id in this case), I want to see that.
My initial thought was to do a count(*) group by row#, seat#, and child. Everything with a count of > 1 would mean it's a dupe and = 1 would mean it's unique. That logic only works if you are comparing single rows though. I need to compare multiple rows. I cannot figure out a way to do it via SQL. The solution I have involves going outside of SQL and works (probably). I'm just wondering if there is a way to do it in DB2.
Does this do what you want?
select d.teacher_id, sa.row_num, sa.seat_num, sa.child_name
from seatingarrangement sa join
data d
on sa.seat_argmt_id = d.seat_argmt_id
group by d.teacher_id, sa.row_num, sa.seat_num, sa.child_name
having count(*) > 1;
EDIT:
If you want to find two arrangements that are the same:
select sa1.seat_argmt_id, sa2.seat_argmt_id
from seatingarrangement sa1 join
seatingarrangement sa2
on sa1.seat_argmt_id < sa2.seat_argmt_id and
sa1.row_num = sa2.row_num and
sa1.seat_num = sa2.seat_num and
sa1.child_name = sa2.child_name
group by sa1.seat_argmt_id, sa2.seat_argmt_id
having count(*) = (select count(*) from seatingarrangement sa where sa.seat_argmt_id = sa1.seat_argmt_id) and
count(*) = (select count(*) from seatingarrangement sa where sa.seat_argmt_id = sa2.seat_argmt_id);
This finds the matches between two arrangements and then verifies that the counts are correct.

Create a combined list from two tables

I have a table with CostCenter_ID (int) and a second table with Process_ID (int).
I'd like to combine the results of both tables so that each cost center ID is assigned to all process IDs, like so:
|CostCenterID | ProcessID |
---------------------------
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
I've done it before but I'm drawing a blank. I've tried this:
SELECT CostCenter_ID,NULL FROM dbo.Cost_Centers
UNION ALL
SELECT NULL,Process_ID FROM dbo.Processes
which returns this:
|CostCenterID | ProcessID |
---------------------------
| 1 | NULL |
| NULL | 1 |
| NULL | 2 |
| NULL | 3 |
Try:
select a.CostCenterID, b.ProcessID
from table1 a
cross join table2 b
or:
select a.CostCenterID, b.ProcessID
from table1 a
,table2 b
NB: cross join is the better method as it makes it clearer to the reader what your intentions are.
More info (with pics) here: http://www.w3resource.com/sql/joins/cross-join.php