SQL merging columns where only one column differ - sql

I use SQL Server 2017 and have a simple query with joins that looks like this.
SELECT DISTINCT
[p].[MainTitle] AS Title,
[pe].[Name] AS JobName,
[ea].[Label] AS Stat
FROM
[Project] AS [P]
INNER JOIN
[ProjectExtended] AS [pe] ON [p].[ID] = [pe].[ProjectID]
INNER JOIN
[ExtAttributes] AS [ea] ON [pe].[AttributeID] = [ea].[ID]
...
WHERE
pe.date BETWEEN #start AND #end
AND [ea].Label IN ('A', 'B', 'C')
The result right know looks like this (JobName is different for every Title but always the same for the same Title)
+-----------+----------+--------------+
| Title | JobName | Stat |
+-----------+----------+--------------+
| BAMB | asdf | C |
| BIRT | fdsa | B |
| BIRT | fdsa | A |
| BOND | lofe | B |
+-----------+----------+--------------+
But I want it to show like this.
+-----------+----------+--------------+
| Title | JobName | Stat |
+-----------+----------+--------------+
| BAMB | asdf | C |
| BIRT | fdsa | B, A |
| BOND | lofe | B |
+-----------+----------+--------------+
I tried with different versions of CONCAT, STRING_AGG functions that you suggested in different threads, but no success. I only got to the point where it joins all the stats in last column - result was like A, B, C in every row.

This should help:
SELECT
[t].[Title] AS Title,
[t].[JobName] AS JobName,
STRING_AGG([t].[Stat], ',') AS Stat
FROM (
SELECT DISTINCT
[p].[MainTitle] AS Title,
[pe].[Name] AS JobName,
[ea].[Label] AS Stat
FROM
[Project] AS [P]
INNER JOIN
[ProjectExtended] AS [pe] ON [p].[ID] = [pe].[ProjectID]
FULL OUTER JOIN
[ExtAttributes] AS [ea] ON [pe].[AttributeID] = [ea].[ID]
...
WHERE
pe.date BETWEEN #start AND #end
AND [ea].Label IN ('A', 'B', 'C', NULL)
) t
GROUP BY
[t].[Title]
, [t].[JobName]
It says version 15 but hopefully is available on yours also
https://learn.microsoft.com/en-us/sql/t-sql/functions/string-agg-transact-sql?view=sql-server-ver15

Related

SQL code to find if a series of lists do NOT contain a particular value

I have two tables
Jobs
+-----+------+
| Job | Name |
+-----+------+
| 1 | Foo |
| 2 | Bar |
| 3 | Baz |
| 4 | Qwe |
+-----+------+
Job_Operations
+-----+--------------+
| Job | Work_Center |
+-----+--------------+
| 1 | SomeCenter |
| 1 | Full Kit |
| 2 | SomeCenter |
| 3 | SomeCenter |
| 3 | Full Kit |
+-----+--------------+
The tables are linked on the Job column. How can I find the entries in Jobs without a corresponding 'Full Kit' entry in Job_Operations?
Desired Results
+-----+------+
| Job | Name |
+-----+------+
| 2 | Bar |
| 4 | Qwe |
+-----+------+
This seems like a straight forward NOT EXISTS query
SELECT J.*
FROM Jobs J
WHERE NOT EXISTS(SELECT *
FROM Job_Operations JO
WHERE JO.Job = J.Job
AND JO.Work_Center = 'Full Kit')
Select * from
(
select Jobs.* , job_Operations.Work_Center as wc
from Jobs
left join Job_Operations on Jobs.Job=Job_Operations.Job and Job_Operations.Work_Center='Full Kit'
) as sub1 where wc is null
In the subselect left join tells the SQL server to give me a row for every row in the Jobs table, even if it does not find a corresponding value in the job_Operations. From job_Operations only rows that contain your 'Full Kit' are regarded for the join. If the join fails, SQLsefer just returns a null for the fields in job_Operations. The outer select just fetches those rows.
Another way is to use Exists, see how that works in the other answer. But if you want to learn SQL try to get an understanding of how left, right inner and outer/full join work.
Simple solution in code below.
Also keep in mind that "working" doesn't meant "high performance".
Check SQL-plan on your specific DB.
select j.*
from job j
where j.job not in (select jo.job
from Job_Operations jo
where jo.Work_Center = 'Full Kit');

SQL MariaDB getting data from 7 tables including mm-tables resulting in too many unwanted rows

I'm struggling with getting data from 7 different sql-tables without receiving too many rows.
I have the following (simple) query which retrieves data from 7 different tables:
SELECT h.name, h.address, h.zipcode, h.city, h.association, r.name_de, f.first_name, f.last_name, f.email, p.year, j.name
FROM `tx_gipdhotels_domain_model_hotel` AS h
JOIN `tx_gipdhotels_hotel_jobs_mm` AS hj ON h.uid = hj.uid_local
JOIN `tx_gipdhotels_domain_model_jobs` AS j ON j.uid = hj.uid_foreign
JOIN `tx_gipdhotels_hotel_participations_mm` AS hp ON h.uid = hp.uid_local
JOIN `tx_gipdhotels_domain_model_participations` AS p ON p.uid = hp.uid_foreign
JOIN `tx_gipdhotels_domain_model_region` AS r ON r.uid = h.region
JOIN `fe_users` AS f ON f.uid = h.feuser
As you can see there are two many-to-many-relationships between the tables. These two tables aren't related (except through the h table). Now the problem is that this results in receiving a row for each possible combination of these mm-tables.
Example:
table 1 hotel
|-----------|------------|----------|----------|
| uid | name | jobs | part |
|...........|............|..........|..........|
| 1 | ab | 3 | 2 |
| | | | |
table 2 jobs
|-----------|------------|
| uid | name |
|...........|............|
| 1 | tech |
| 2 | cs |
| 3 | perf |
| | |
table 3 part
|-----------|------------|
| uid | name |
|...........|............|
| 1 | abcd |
| 2 | efgh |
| | |
With this combination (including mm-tables for hotel_jobs and hotel_part) I would receive 6 rows for one hotel only and in each row only one value would differ from another row:
result:
|-----------|------------|----------|----------|
| uid | name | job | part |
|...........|............|..........|..........|
| 1 | ab | tech | abcd |
| 1 | ab | tech | defg |
| 1 | ab | cs | abcd |
| 1 | ab | cs | defg |
| 1 | ab | perf | abcd |
| 1 | ab | perf | defg |
| | | | |
It would be lovely if I could retrieve this data in one single row like the following:
wanted result:
|-----------|------------|--------------------|----------------|
| uid | name | job | part |
|...........|............|....................|................|
| 1 | ab | tech, cs, perf | abcd, efgh |
| | | | |
I can't figure out how to get the wanted result, it exceeds my experience and knowledge so I'm asking you, do you know how to achieve this with a single query?
I've googled quite a bit and I have found the STUFF() method but it's not supported in MariaDB. In some question here on stack someone has done something similar with a cast but I didn't understand it too well and I didn't know how to adapt this to my problem...
I'm using MariaDB and the query will be made from php. There is no way of changing the data structure of the tables.
Any help and explanations would be greatly appreciated.
I hope this will work, try it, if there is any error, we are gonna fix it.
SELECT
h.name,
h.address,
h.zipcode,
h.city,
h.association,
GROUP_CONCAT(DISTINCT p.year SEPARATOR ', '),
GROUP_CONCAT(DISTINCT j.name SEPARATOR ', '),
r.name_de,
f.first_name,
f.last_name,
f.email,
h.tstamp,
h.crdate
FROM tx_gipleasedisturbhotels_domain_model_hotel AS h
JOIN `tx_gipleasedisturbhotels_hotel_jobs_mm` AS hj
ON h.uid = hj.uid_local
JOIN `tx_gipleasedisturbhotels_domain_model_jobs` AS j
ON j.uid = hj.uid_foreign
JOIN `tx_gipleasedisturbhotels_hotel_participations_mm` AS hp
ON h.uid = hp.uid_local
JOIN `tx_gipleasedisturbhotels_domain_model_participations` AS p
ON p.uid = hp.uid_foreign
JOIN `tx_gipleasedisturbhotels_domain_model_region` AS r
ON r.uid = h.region
JOIN `fe_users` AS f
ON f.uid = h.feuser
GROUP BY h.name
ORDER BY h.name ASC
Thanks to #jarlh I found the solution:
SELECT h.name, h.address, h.zipcode, h.city, h.association,
GROUP_CONCAT(DISTINCT p.year SEPARATOR ', '),
GROUP_CONCAT(DISTINCT j.name SEPARATOR ', '),
r.name_de, f.first_name, f.last_name, f.email, h.tstamp, h.crdate
FROM `tx_gipleasedisturbhotels_domain_model_hotel` AS h
JOIN `tx_gipleasedisturbhotels_hotel_jobs_mm` AS hj ON h.uid = hj.uid_local
JOIN `tx_gipleasedisturbhotels_domain_model_jobs` AS j ON j.uid = hj.uid_foreign
JOIN `tx_gipleasedisturbhotels_hotel_participations_mm` AS hp ON h.uid = hp.uid_local
JOIN `tx_gipleasedisturbhotels_domain_model_participations` AS p ON p.uid = hp.uid_foreign
JOIN `tx_gipleasedisturbhotels_domain_model_region` AS r ON r.uid = h.region
JOIN `fe_users` AS f ON f.uid = h.feuser
GROUP BY h.name
ORDER BY h.name ASC
It's a combination of GROUP_CONCAT and GROUP BY. It has to be grouped by the field which you want to have only once. To get all mm-values to one single cell you'll have to use GROUP_CONCAT on those fields in the SELECT statement.
With this query I receive the wanted result. Maybe this will be helpful to someone else as well. ;)

SQL Query manipulation

I have three tables :
BookingNode , Booking AirTrip
AirTrip :
+----+------------+
| ID | Name |
+----+------------+
| 0 | One way |
| 1 | Round trip |
| 2 | Circle |
| 3 | Other |
+----+------------+
When ever we make a booking we store the data as :
BookingNode table
+--------+-------------------+------------+----------------------+
| ID | CustomerGivenName | IPAddress | Email |
+--------+-------------------+------------+----------------------+
| 177022 | xfghfh | 2130706473 | mikehussey#gmail.com |
| 177021 | cfggjfj | 2130706473 | mikehussey#gmail.com |
+--------+-------------------+------------+----------------------+
Booking Table :
+--------+---------------+-----------+------------+------------+
| ID | BookingNodeID | AirTripID | AirLineId | Provider |
+--------+---------------+-----------+------------+------------+
| 181251 | 177020 | 1 | 978 | Jet |
| 181252 | 177021 | 0 | 982 | Go |
| 181253 | 177021 | 0 | 978 | Jet |
+--------+---------------+-----------+------------+------------+
If round trip flight is booked and ProviderID is same then a single entry is done in Booking Table with AirTripID value as 1.(Booking ID : 181251 and Provider Jet )
But if providers are different for both the legs then two entries are done in Booking Table with AirTripID for both entries are one(Booking ID : 181252 and 181253 Provider Go,Jet ).In this case BookingNodeID value being same.
Prob : I have to write a query to get different type of Bookings.(Oneway, RoundTrip,Circle).But when I apply join on AirTripID , it is giving me incorrect results.
How can I write my query to give correct results knowing that BookingNodeID is going to be the same for roundtrip (both entries in Booking Table)
Sample Output
+-------------+---------------+-------------------+------------+
| AirTripName | BookingNodeID | CustomerGivenName | IPAddress |
+-------------+---------------+-------------------+------------+
| TwoWay | 177020 | xfghfh | 2130706473 |
| TwoWay | 177021 | cfggjfj | 2130706473 |
+-------------+---------------+-------------------+------------+
Basically, this code might have an error due to my laziness syntom of data entry. But, the logic of the query is, if b.AirTripID is 0, add extra condition which group by Booking. if result return more than 1 row, is actually 2 way. so AirTripType will become 1, otherwise, remain the same as b.AirTripID. You may copy below on and try fix if theres any error. i believe the logic should work based on your expected result.
select
bd.ID,
bd.CustomerGivenName,
case b.AirTripID
when 1 then 1
when 2 then 2
when 3 then 3
when 0 then
case select BookingNodeID
from Booking
where Booking.BookingNodeID = bd.ID group by BookingNodeID having Count(BookingNodeID)
when 1 then 1
else 0 end as AirTripType,
bd.IPAddress
from BookingNode bd
inner join (select BookingNodeID ,AirTripID from Booking group by BookingNodeID ,AirTripID) as b on b.BookingNodeID = bd.ID
where id=177021
Try This
WITH CTE
AS
(
SELECT
SeqNo = ROW_NUMBER() OVER(PARTITION BY BN.ID ORDER BY B.ID),
B.BookingNodeID,
BN.CustomerGivenName,
BN.IPAddress,
AirTripId = A.ID,
AirTripNm = A.Name
FROM Booking B
INNER JOIN AirTrip A
ON A.ID = B.AirTripID
LEFT JOIN BookingNode BN
ON B.BookingNodeID = BN.id
)
SELECT
C1.SeqNo,
AirTripName = CASE WHEN C2.SeqNo IS NOT NULL
THEN 'Round trip'
ELSE C1.AirTripNm END,
C1.BookingNodeID,
C1.CustomerGivenName,
C1.IPAddress
FROM CTE C1
LEFT JOIN CTE C2
ON C1.BookingNodeID = C2.BookingNodeID
AND C2.SeqNo = 2
WHERE c1.SeqNo = 1
SQL Fiddle Link Here
Select distinct bk.bookingnodeid,cst.customername,ipaddress,
case when count(airtripid)over(partition by bookingnodeid order by bookingnodeid)=2 then 'RoundTrip' else name end As AirTripName
from booking bk
inner join airlinetrip at
on bk.airtripid=at.id
inner join customer cst
on cst.id=bk.bookingnodeid

Use JOIN on multiple columns multiple times

I am trying to figure out the best way to use a JOIN in MSSQL in order to do the following:
I have two tables. One table contains technician IDs and an example of one data set would be as follows:
+--------+---------+---------+---------+---------+
| tagid | techBid | techPid | techFid | techMid |
+--------+---------+---------+---------+---------+
| 1-1001 | 12 | 0 | 11 | 6 |
+--------+---------+---------+---------+---------+
I have another table that stores the names of these technicians:
+------+-----------+
| TTID | SHORTNAME |
+------+-----------+
| 11 | Steven |
| 12 | Mark |
| 6 | Pierce |
+------+-----------+
If the ID of a technician in the first table is 0, there is no technician of that type for that row (types are either B, P, F, or M).
I am trying to come up with a query that will give me a result that contains all of the data from table 1 along with the shortnames from table 2 IF there is a matching ID, so the result would look something like the following:
+--------+---------+---------+---------+---------+----------------+----------------+----------------+----------------+
| tagid | techBid | techPid | techFid | techMid | techBShortName | techPShortName | techFShortName | techMShortName |
+--------+---------+---------+---------+---------+----------------+----------------+----------------+----------------+
| 1-1001 | 12 | 0 | 11 | 6 | Mark | NULL | Steven | Pierce |
+--------+---------+---------+---------+---------+----------------+----------------+----------------+----------------+
I am trying to use a JOIN to do this, but I cannot figure out how to join on multiple columns multiple times to where it would look something like
Select table1.tagid, table1.techBid, table1.techPid, table1.techFid, table1.techMid, table2.shortname
FROM table1
INNER JOIN table2 on //Dont know what to put here
You need to use left joins like this:
Select table1.tagid, table1.techBid, table1.techPid, table1.techFid, table1.techMid,
t2b.shortname, t2p.shortname, t2f.shortname, t2m.shortname,
FROM table1
LEFT JOIN table2 t2b on table1.techBid = t2b.ttid
LEFT JOIN table2 t2p on table1.techPid = t2p.ttid
LEFT JOIN table2 t2f on table1.techFid = t2f.ttid
LEFT JOIN table2 t2m on table1.techMid = t2m.ttid
you just do mutiple left join
select tech.techPid, techPname.SHORTNAME
, tech.techFid, techFname.SHORTNAME
from tech
left join techName as techPname
on tech.techPid = techPname.TTID
left join techName as techFname
on tech.techFid = techFname.TTID

Sql query listing Fathers and childs with joins, how to distinct them?

Having those tables:
table_n1:
| t1_id | t1_name |
| 1 | foo |
table_n2:
| t2_id | t1_id | t2_name |
| 1 | 1 | bar |
I need a query that gives me two result:
| names |
| foo |
| foo / bar |
But i cant figure out the right way.
I wrote this one:
SELECT
CONCAT_WS(' / ', table_n1.t1_name, table_n2.t2_name) AS names
FROM
table_n1
LEFT JOIN table_n2 ON table_n2.t1_id = table_n1.t1_id
that works for an half: this only return the 2° row (in the example above):
| names |
| foo - bar |
This query return the 'father' (table_n1) name only when it doesnt have 'childs' (table_n2).
How can i fix it?
Using a UNION and changing the LEFT JOIN to an INNER JOIN should give you the correct result.
SELECT table_n1.t1_name AS names
FROM table_n1
UNION ALL
SELECT CONCAT_WS(' / ', table_n1.t1_name, table_n2.t2_name) AS names
FROM table_n1
INNER JOIN table_n2 ON table_n2.t1_id = table_n1.t1_id