Join columns of SQL queries together - sql

I've got the below two queries which I'm trying to figure out if I can merge into a single query. Both tables have the exact same three columns, but the organization names will differ between the two in many cases. The queries are like so:
SELECT OrganizationHierarchyUnitLevelThreeNm, COUNT(*) AS Complete
FROM #complete c
JOIN #wanted w ON w.WorkerKey = c.WorkerKey
GROUP BY OrganizationHierarchyUnitLevelThreeNm
ORDER BY 1;
SELECT OrganizationHierarchyUnitLevelThreeNm, COUNT(*) AS Wanted
FROM #wanted
GROUP BY OrganizationHierarchyUnitLevelThreeNm
ORDER BY 1;
So the first one might end up with something like this:
OrganizationHierarchyUnitLevelThreeNm | Complete
------------------------------------------------
Foo | 2
Bar | 17
and then the second would would be
OrganizationHierarchyUnitLevelThreeNm | Wanted
------------------------------------------------
Foo | 27
Baz | 132
So in the resulting query I'd want:
OrganizationHierarchyUnitLevelThreeNm | Wanted | Complete
---------------------------------------------------------
Foo | 27 | 2
Bar | 0 | 17
Baz | 132 | 0
Is that possible?

SELECT OrganizationHierarchyUnitLevelThreeNm,
Count(w.WorkerKey) as wanted,
COUNT(c.WorkerKey) AS Complete
FROM #wanted w
LEFT JOIN #complete c ON w.WorkerKey = c.WorkerKey
GROUP BY OrganizationHierarchyUnitLevelThreeNm
ORDER BY 1;

You can use union all:
select OrganizationHierarchyUnitLevelThreeNm, sum(Wanted), sum(Complete)
from ( (select OrganizationHierarchyUnitLevelThreeNm, 1 as Wanted , 0 as Complete
from #wanted w
) union all
(select OrganizationHierarchyUnitLevelThreeNm, 0 as Wanted , 1 as Complete
from #complete c
)
) t
group by OrganizationHierarchyUnitLevelThreeNm;

Related

SQL - Distinct count between two tables

I'm having a mind lapse on what I believe is a relatively easy script. Hopefully I'm overthinking the logic.
What I'm trying to do is perform two counts on a distinct column which is right joined.
What I want is:
count(a.book_id) as count_of_books
count(b.book_ref_number) as count_of_losses
Expected Output
--------------------------------------------------------
| Book | count_of_books | count of losses|
--------------------------------------------------------
|Hunger Games | 76 | 31 |
--------------------------------------------------------
|Hop on Pop | 27 | 6 |
--------------------------------------------------------
|Pout Pout Fish | 138 | 43 |
--------------------------------------------------------
I have tried a couple different scripts. Here are the two scripts I've tried.
(select count(*) from Inventory_Table x ) Count1,
(select count(*) from Loss_table b ) Count2
from Inventory_Table x
right join Loss_table b on b.book_ref_number = x.book_id
where rownum < 20
select
a.book_name,
count(distinct a.book_id),
count(b.book_ref_number)
from Inventory_Table x
right join Loss_table b on trim(b.book_ref_number) = trim(a.book_id)
Results I get
--------------------------------------------------------
| Book | count_of_books | count of losses|
--------------------------------------------------------
|Moby Dick | 4376 | 2574 |
--------------------------------------------------------
I'm looking for guidance in my neglectful mistake. Thank you in advance
and rownum <20 doesn't make sense. you are limiting your result set with 20 records.
try this:
select * from (
select
a.mrch_Nr,
count(distinct a.fdr_trac_nr),
count(b.auth_id)
from DATASTORE_FD.DEB_CRD_AUTH_LOG_REC a
right join jordab26.ft b on trim(b.auth_id) = trim(a.fdr_trac_nr)
where a.auth_log_dt between '20200101' and '20200408'
group by a.mrch_nr
)
where rownum < 20
Try this, I'm not sure about rownum < 20. Also, make sure your add correct group by condition.
select sum(case book_id when null then 0 else 1 end ) count_of_books,
sum(case book_ref_number when null then 0 else 1 end ) count_of_losses
from Inventory_Table x
right join Loss_table b on b.book_ref_number = x.book_id
where rownum < 20
Is this what you want?
Select distinct bookname,
count(distinct
a.bookid)+sum(
case when a.bookid IS NULL
THEN 1 END) ,
count(distinct b.id) as lossid
From inventary_table a
Left Join
Loss_table b
On
a.bookid=b.book_ref_number
SELECT book_name,COUNT(book_id),COUNT(book_ref_id) FROM Inventory_Table right join Loss_table on book_ref_number = book_id GROUP BY book_name
But if you need all the books in Inventory and only matching books from Loss_table then it should be left join:
SELECT book_name,COUNT(book_id),COUNT(book_ref_id) FROM Inventory_Table leftjoin Loss_table on book_ref_number = book_id GROUP BY book_name
0
SELECT book_name,COUNT(book_id),COUNT(book_ref_id)
FROM Inventory_Table
right join Loss_table on book_ref_number = book_id GROUP BY book_name

Comparing different columns in SQL for each row

after some transformation I have a result from a cross join (from table a and b) where I want to do some analysis on. The table for this looks like this:
+-----+------+------+------+------+-----+------+------+------+------+
| id | 10_1 | 10_2 | 11_1 | 11_2 | id | 10_1 | 10_2 | 11_1 | 11_2 |
+-----+------+------+------+------+-----+------+------+------+------+
| 111 | 1 | 0 | 1 | 0 | 222 | 1 | 0 | 1 | 0 |
| 111 | 1 | 0 | 1 | 0 | 333 | 0 | 0 | 0 | 0 |
| 111 | 1 | 0 | 1 | 0 | 444 | 1 | 0 | 1 | 1 |
| 112 | 0 | 1 | 1 | 0 | 222 | 1 | 0 | 1 | 0 |
+-----+------+------+------+------+-----+------+------+------+------+
The ids in the first column are different from the ids in the sixth column.
In a row are always two different IDs that are matched with each other. The other columns always have either 0 or 1 as a value.
I am now trying to find out how many values(meaning both have "1" in 10_1, 10_2 etc) two IDs have on average in common, but I don't really know how to do so.
I was trying something like this as a start:
SELECT SUM(CASE WHEN a.10_1 = 1 AND b.10_1 = 1 then 1 end)
But this would obviously only count how often two ids have 10_1 in common. I could make something like this for example for different columns:
SELECT SUM(CASE WHEN (a.10_1 = 1 AND b.10_1 = 1)
OR (a.10_2 = 1 AND b.10_1 = 1) OR [...] then 1 end)
To count in general how often two IDs have one thing in common, but this would of course also count if they have two or more things in common. Plus, I would also like to know how often two IDS have two things, three things etc in common.
One "problem" in my case is also that I have like ~30 columns I want to look at, so I can hardly write down for each case every possible combination.
Does anyone know how I can approach my problem in a better way?
Thanks in advance.
Edit:
A possible result could look like this:
+-----------+---------+
| in_common | count |
+-----------+---------+
| 0 | 100 |
| 1 | 500 |
| 2 | 1500 |
| 3 | 5000 |
| 4 | 3000 |
+-----------+---------+
With the codes as column names, you're going to have to write some code that explicitly references each column name. To keep that to a minimum, you could write those references in a single union statement that normalizes the data, such as:
select id, '10_1' where "10_1" = 1
union
select id, '10_2' where "10_2" = 1
union
select id, '11_1' where "11_1" = 1
union
select id, '11_2' where "11_2" = 1;
This needs to be modified to include whatever additional columns you need to link up different IDs. For the purpose of this illustration, I assume the following data model
create table p (
id integer not null primary key,
sex character(1) not null,
age integer not null
);
create table t1 (
id integer not null,
code character varying(4) not null,
constraint pk_t1 primary key (id, code)
);
Though your data evidently does not currently resemble this structure, normalizing your data into a form like this would allow you to apply the following solution to summarize your data in the desired form.
select
in_common,
count(*) as count
from (
select
count(*) as in_common
from (
select
a.id as a_id, a.code,
b.id as b_id, b.code
from
(select p.*, t1.code
from p left join t1 on p.id=t1.id
) as a
inner join (select p.*, t1.code
from p left join t1 on p.id=t1.id
) as b on b.sex <> a.sex and b.age between a.age-10 and a.age+10
where
a.id < b.id
and a.code = b.code
) as c
group by
a_id, b_id
) as summ
group by
in_common;
The proposed solution requires first to take one step back from the cross-join table, as the identical column names are super annoying. Instead, we take the ids from the two tables and put them in a temporary table. The following query gets the result wanted in the question. It assumes table_a and table_b from the question are the same and called tbl, but this assumption is not needed and tbl can be replaced by table_a and table_b in the two sub-SELECT queries. It looks complicated and uses the JSON trick to flatten the columns, but it works here:
WITH idtable AS (
SELECT a.id as id_1, b.id as id_2 FROM
-- put cross join of table a and table b here
)
SELECT in_common,
count(*)
FROM
(SELECT idtable.*,
sum(CASE
WHEN meltedR.value::text=meltedL.value::text THEN 1
ELSE 0
END) AS in_common
FROM idtable
JOIN
(SELECT tbl.id,
b.*
FROM tbl, -- change here to table_a
json_each(row_to_json(tbl)) b -- and here too
WHERE KEY<>'id' ) meltedL ON (idtable.id_1 = meltedL.id)
JOIN
(SELECT tbl.id,
b.*
FROM tbl, -- change here to table_b
json_each(row_to_json(tbl)) b -- and here too
WHERE KEY<>'id' ) meltedR ON (idtable.id_2 = meltedR.id
AND meltedL.key = meltedR.key)
GROUP BY idtable.id_1,
idtable.id_2) tt
GROUP BY in_common ORDER BY in_common;
The output here looks like this:
in_common | count
-----------+-------
2 | 2
3 | 1
4 | 1
(3 rows)

Returning rows that contain two values within the table

I feel like I'm taking crazy pills.
I have a table called problems. It looks like:
id | pid | problem_list_id
-----------------------------------
1 | 159 | 16
2 | 159 | 4
3 | 10 | 16
4 | 565 | 90
5 | 565 | 4
6 | 122 | 12
I'd like to query pid that have a problem_id of BOTH 16 and 4.
SELECT
YEAR(pbd.date_created),
COUNT(*)
FROM
patient_booking_data pbd inner join problems p on pbd.pid = p.pid
WHERE p.problem_list_id in (16,4)
GROUP BY YEAR(pbd.date_created)
ORDER BY YEAR(pbd.date_created)
This obviously would hit 16 or 4. I need pid's with BOTH 16 AND 4.
When I try using:
WHERE (p.problem_list_id = 16 AND p.problem_list_id = 4)
That doesn't yield any results.
I can use something like:
SELECT
YEAR(pbd.date_created),
COUNT(*)
FROM
patient_booking_data pbd inner join problems p on pbd.pid = p.pid
WHERE p.problem_list_id = 16
AND pbd.pid in (SELECT pid FROM problems where problem_list_id = 4)
GROUP BY YEAR(pbd.date_created)
ORDER BY YEAR(pbd.date_created)
But using a nested select seems like overkill.
Am I missing something obvious?
Im not sure why your query is different than your data table?
But with your data table you use HAVING COUNT()
SELECT pid
FROM problems
WHERE problem_list_id in (16,4)
GROUP BY pid
HAVING COUNT(DISTINCT problem_list_id) = 2
I use distinct in case a pid can have muliple occurence of 16 or 2 otherwise just COUNT(*) will be enough
Maybe you need something like this. Using cte to calculate the pid first and then return the patient_booking
WITH cte as (
SELECT pid
FROM problems
WHERE problem_list_id in (16,4)
GROUP BY pid
HAVING COUNT(DISTINCT problem_list_id) = 2
)
SELECT
YEAR(pbd.date_created),
COUNT(*)
FROM patient_booking_data pbd
JOIN CTE c on pbd.pid = c.pid
GROUP BY YEAR(pbd.date_created)
ORDER BY YEAR(pbd.date_created)

Return count(*) even if 0

I have the following query:
select bb.Name, COUNT(*) as Num from BOutcome bo
JOIN BOffers bb ON bo.ID = bb.BOutcomeID
WHERE bo.EventID = 123 AND bo.OfferTypeID = 321 AND bb.NumA > bb.NumB
GROUP BY bb.Name
The table looks like:
Name | Num A | Num B
A | 10 | 3
B | 2 | 3
C | 10 | 3
A | 9 | 3
B | 2 | 3
C | 9 | 3
The expected output should be:
Name | Count
A | 2
B | 0
C | 2
Because when name is A and C then Num A is bigger to times than Num B and when Name is B, in both records Num A is lower than Num B.
My current output is:
Name | Count
A | 2
C | 2
Because B's output is 0, i am not getting it back in my query.
What is wrong with my query? how should I get it back?
Here is my guess. I think this is a much simpler approach than all of the left/right join hoops people have been spinning their wheels on. Since the output of the query relies only on columns in the left table, there is no need for an explicit join at all:
SELECT
bb.Name,
[Count] = SUM(CASE WHEN bb.NumA > bb.NumB THEN 1 ELSE 0 END)
-- just FYI, the above could also be written as:
-- [Count] = COUNT(CASE WHEN bb.NumA > bb.NumB THEN 1 END)
FROM dbo.BOffers AS bb
WHERE EXISTS
(
SELECT 1 FROM dbo.BOutcome
WHERE ID = bb.BOutcomeID
AND EventID = 123
AND OfferTypeID = 321
)
GROUP BY bb.Name;
Of course, we're not really sure that both Name and NumA/NumB are in the left table, since the OP talks about two tables but only shows one table in the sample data. My guess is based on the query he says is "working" but missing rows because of the explicit join.
Another wild guess. Feel free to downvote:
SELECT ba.Name, COUNT(bb.BOutcomeID) as Num
FROM
( SELECT DISTINCT ba.Name
FROM
BOutcome AS b
JOIN
BOffers AS ba
ON ba.BOutcomeID = b.ID
WHERE b.EventID = 123
AND b.OfferTypeID = 321
) AS ba
LEFT JOIN
BOffers AS bb
ON AND bb.Name = ba.Name
AND bb.NumA > bb.NumB
GROUP BY ba.Name ;

SQL:Query to check if a column meets certain criteria, if it does perform one action if it doesn't perform another

I have found it quite hard to word what I want to do in the title so I will try my best to explain now!
I have two tables which I am using:
Master_Tab and Parts_Tab
Parts_Tab has the following information:
Order_Number | Completed| Part_Number|
| 1 | Y | 64 |
| 2 | N | 32 |
| 3 | Y | 42 |
| 1 | N | 32 |
| 1 | N | 5 |
Master_Tab has the following information:
Order_Number|
1 |
2 |
3 |
4 |
5 |
I want to generate a query which will return ALL of the Order_Numbers listed in the Master_Tab on the following conditions...
For each Order_Number I want to check the Parts_Tab table to see if there are any parts which aren't complete (Completed = 'N'). For each Order_Number I then want to count the number of uncompleted parts an order has against it. If an Order_Number does not have uncompleted parts or it is not in the Parts_Table then I want the count value to be 0.
So the table that would be generated would look like this:
Order_Number | Count_of_Non_Complete_Parts|
1 | 2 |
2 | 1 |
3 | 0 |
4 | 0 |
5 | 0 |
I was hoping that using a different kind of join on the tables would do this but I am clearly missing the trick!
Any help is much appreciated!
Thanks.
I have used COALESCE to convert NULL to zero where necessary. Depending on your database platform, you may need to use another method, e.g. ISNULL or CASE.
select mt.Order_Number,
coalesce(ptc.Count, 0) as Count_of_Non_Complete_Parts
from Master_Tab mt
left outer join (
select Order_Number, count(*) as Count
from Parts_Tab
where Completed = 'N'
group by Order_Number
) ptc on mt.Order_Number = ptc.Order_Number
order by mt.Order_Number
You are looking for a LEFT JOIN.
SELECT mt.order_number, count(part_number) AS count_noncomplete_parts
FROM master_tab mt LEFT JOIN parts_tab pt
ON mt.order_number=pt.order_number AND pt.completed='N'
GROUP BY mt.order_number;
It is also possible to put pt.completed='N' into a WHERE clause, but you have to be careful of NULLs. Instead of the AND you can have
WHERE pt.completed='N' OR pr.completed IS NULL
SELECT mt.Order_Number SUM(tbl.Incomplete) Count_of_Non_Complete_Parts
FROM Master_Tab mt
LEFT JOIN (
SELECT Order_Number, CASE WHEN Completed = 'N' THEN 1 ELSE 0 END Incomplete
FROM Parts_Tab
) tbl on mt.Order_Number = tbl.Order_Number
GROUP BY mt.Order_Number
Add a WHERE clause to the outer query if you need to filter for specific order numbers.
I think it's easiest to get a subquery in there. I think this should be self-explanitory, if not feel free to ask any questions.
CREATE TABLE #Parts
(
Order_Number int,
Completed char(1),
Part_Number int
)
CREATE TABLE #Master
(
Order_Number int
)
INSERT INTO #Parts
SELECT 1, 'Y', 64 UNION ALL
SELECT 2, 'N', 32 UNION ALL
SELECT 3, 'Y', 42 UNION ALL
SELECT 1, 'N', 32 UNION ALL
SELECT 1, 'N', 5
INSERT INTO #Master
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5 UNION ALL
SELECT 6
SELECT M.Order_Number, ISNULL(Totals.NonCompletedCount, 0) FROM #Master M
LEFT JOIN (SELECT P.Order_Number, COUNT(*) AS NonCompletedCount FROM #Parts P
WHERE P.Completed = 'N'
GROUP BY P.Order_Number) Totals ON Totals.Order_Number = M.Order_Number