Say I have a following table:
table1
Now I want to have a permutation within cid column for each two ids (lets say id1=A and id2=B), and try to calculate the intersection of pid of A and B divided by the union of pid of A and B. For example, let A = cid 1 and B = cid 2, then the answer is intersection/union = 0/(1+1)=0 (notice B has purchased pid 3 for three times, but same product count only once).
The result should be like:
result
There is going to be a permutation of any 2 different numbers in table1.cid, where smaller number goes before larger number, as well as the result of intersection/union. How can I write a query to perfome that?
Both union and intersection can be achieved by joining the table with itself on the PID column:
SELECT a.cid a, b.cid b, COUNT(*) intersection
FROM table a
INNER JOIN table b
ON a.pid = b.pid AND a.cid < b.cid
if you use an OUTER join, that count(*) will return the size of the union.
you can execute both queries, then join on CIDs to have both intersection and union available for the computation:
SELECT a, b, intersection/union
FROM (<intersection query>) i
JOIN (<union query>) u
ON i.a = u.a AND i.b = u.b
You could of course optimize this a little bit since the inner join is a subset of the outer join, so there's no real need to perform both, but that's a matter for another question...
Related
I need to fetch data from 5 tables(all columns of each table) all have FK, which is PK of single table.
But some of the tables may have record may be empty.If data is present on the respective column/table it should return otherwise null/default value
There is one to many and one to one relations on the child tables with parent table.
I have tried so far
- UNION which has concern of same number of columns
- CROSS JOIN not returning any data
- SELECT ALL_COLUMN FROM ALL_TABLE WHERE TABLE.FK=ID Not returning any data
- LEFT JOIN working for 2 tables but not more than that
SELECT A.GENDER, B.BLOCKED_USER FROM t_macroworld_registration AS A
LEFT JOIN t_macroworld_blacklist AS B ON 1=1 WHERE A.ID=15
What are the possible ways I can implement this in a view in MySQL.
Outer join operations are the normative pattern...
SELECT ...
FROM a
LEFT JOIN b ON b.a_id = a.id
LEFT JOIN c ON c.a_id = a.id
LEFT JOIN d ON d.a_id = a.id
WHERE a.id = 15
It's important for the predicates on the outer joined tables to be in the ON clause and not the WHERE clause. If there's any predicate in the WHERE clause requires that a value from one of the outer joined tables be non-NULL, that will negate the "outerness" of the join, making it into an inner join.
The "big rock" problem with this the result when there are more than one matching rows in b, c and d. If there's five rows from b that match, and three rows from c that match, and two rows from b that match, it's going to look like a lot of duplicates. (5x3x2 = 30 rows to be returned, with a lot of duplicated data on those rows.)
Finally I have solved It,
I broke the whole thing into many select query based on FK from each table, so number of additional row returns and mapping has become easy.
Who ever is getting this kind of problem, if it is possible then break it into many select query instead of one.
SELECT
w.id,w.name,a.address,i.name,i.quantity
FROM
warehouse w
LEFT JOIN address AS a ON a.warehouse_id = w.id
LEFT JOIN item AS i ON i.warehouse_id = w.id
LEFT JOIN order AS o ON o.item_id = i.id
WHERE
w.id = 1
GROUP BY 1,2,3,4;
Will give you an overview of your stock and orders for your warehouses. This will also duplicate some results.
Assuming 2 warehouses, 1 address for each, 3 items per warehouse, 2 orders by item = 2 * 3 * 2 = 12 lines
I recommend adding your LEFT JOIN stage by stage and visualizing the result for each stage. You'll quickly understand why lines are multiplying.
Note the usage of foreign keys and ids in the tables to link the tables.
Good luck
I am trying to join 5 tables together, this brings back multiple duplicates of each row, sometimes the result set will be 150+ rows, which isn't expected, I expect somewhere between 5-7
SELECT DISTINCT
a.user_name, a.date_added, a.guide_title, a.guide_summary, a.before_photo, a.after_photo,
b.product_id,
c.step_number, c.photo, c.photo_caption, c.step_title, c.step_description,
d.tip_text,
e.product_name, e.page_address
FROM customer_stories AS a
INNER JOIN customer_stories_products_used AS b ON a.unique_id = b.story_id
INNER JOIN customer_stories_steps AS c ON a.unique_id = c.story_id
INNER JOIN customer_stories_tips AS d ON a.unique_id = d.story_id
INNER JOIN products AS e ON b.product_id = e.product_id
WHERE a.unique_id = 87
I have also tried GROUP BY, but this just brings back a single row
EDIT:
some info of what the expected results should be:
Each customer_story may have multiple steps (table c), multiple tips(table d) and multiple products used (table b), each of these products used should bring back some information from the products table (e).
each row returned brings back all of the data from (table a) which is always the same, and will also contain data from the other tables multiple times
Given two tables A and B, I want all the records from A where A.Param = "X". I also want a LEFT JOIN on B where B contains records of trials by various A.Ids for various experiments m, n, o...
The records on B have a time stamp as B.TrialTime (DateTime). As the design goes, there can be multiple trials for the same experiment by the same A.Id in B and for the purposes of the LEFT JOIN I need only the latest trial. This is what I came up with:
SELECT A.*, B.Experiment, B.Response, B.Evaluation, MAX(B.TrialTime) FROM A
LEFT JOIN B ON B.UserID = A.ID
WHERE A.Param = "X" GROUP BY concat(B.UserID, B.Experiment)
The problem is, it no longer acts as a LEFT JOIN, i.e. I am not getting all the users from A, even if they don't have any record in B, which is what I need. Any help?
Without access to the specifics of your tables, the problem you face is knowing "the most recent" trial (in table B)
In many rdbms one can use ROW_NUMBER() to solve this problem, but as yet MySQL does not supply that useful function. So you need to do something like this:
SELECT
a.*
, b.*
FROM A
LEFT JOIN
(
SELECT
bb.*
FROM B bb
INNER JOIN
(
SELECT
UserID
, MAX(TrialTime) MostRecent
FROM B
GROUP BY B.UserID
) bmax ON bb.UserID = bmax.UserID
AND bb.TrialTime = bmax.MostRecent
) b ON B.UserID = A.ID
Basically it isn't possible to get the MAX() of some columns, and all the other column values "from that row", at the same time. You need to get the MAX(TrialTime) then join back to the same table to get the appropriate rows.
For comparison, if row_number() was available, this is how it might be done:
SELECT
a.*
, b.*
FROM A
LEFT JOIN
(
SELECT
bb.*
, ROW_NUMBER() OVER (PARTITION BY bb.userid
ORDER BY bb.TrialTime DESC) AS rn
FROM B bb
) b ON B.UserID = A.ID
AND b.rn = 1
By the way, there isn't any specific reason given for using a LEFT [OUTER] JOIN. If you want rows from A that have NO joined data in B, then you would need an outer join, otherwise you should use an INNER JOIN for efficiency.
I have 4 MySQL tables on identity column is in common between all these tables,
Tables in Sequence:
1- Items.
2- Sales.
3- Puchases.
4- Returned.
ItemID appears in all of these tables, WHEN i use LEFT JOIN i get duplicates like:
select
a.ItemID AS ItemID,
a.Item_title AS ItemTitle,
SUM(b.qty) AS SoldQty,
SUM(c.qty) AS PurQty,
SUM(d.qty) AS RetQty
from items a
left join sales b on a.ItemID = b.items_ItemID
left join purchases c on a.ItemID = c.items_itemID
left join returned d on a.ItemID = d.items_ItemID
group by a.ItemID
That query was one of the many tries that i've tried :D the result i get is always unique for sales but duplicates for other tables ..
Thanks for the answer.
If I understand what you're trying to do correctly, you want the total number of sales, purchases, returned etc per item id from a. If that's the case, try thinking about an easier piece of the problem first: how do I get the sum of each sales quantity, grouped by item id?
To do that you'd do something like:
select b.items_ItemID, sum(b.qty) as total_sales_qty
from sales b group by b.items_ItemID
You could do the same thing for tables c and d.
Once you've got those, you can join them all together like this:
select a.ItemID, bb.total_sales_qty, cc.total_purchases_qty, dd.total_returned_qty
from items a
left join (
select b.items_ItemID, sum(b.qty) as total_sales_qty
from sales b group by b.items_ItemID) bb
on a.ItemID = bb.items_ItemID
left join (
select c... etc) cc
on a.ItemID = cc.items_ItemID
... etc
I don't really use MySQL, but in SQL Server, you could use the distinct directive, so duplicate rows appear as just a single row. All the returned columns must be identical, mind you.
Select Distinct a.ItemID AS ItemID,... From....
Hope this helps
suppose you have the following table named Likes:
A|B
---
a|b
a|f
a|e
a|i
b|a
b|i
c|d
e|p
In this table, values in A represent people who "like" people in B. So, a likes b, a likes f, a likes e, and so forth. How do you write a query such that you get the number of different users who are two degrees of separation from each user? So as an example, if a likes b, then b is one degree of separation from a. If a likes b, and b likes c, then c is two degrees of separation from a. One more example, if a likes b, and b likes a, then a is two degrees of separation from itself (we don't exclude cycles). So the output should be something like this:
User|CountOfUsersWhoAreTwoDegreesFromUser
-----------------------------------------
a | -
b | -
c | -
e | -
Now, I'm not sure what our counts would be for each user, so I didn't write it in the above table. Also, no persons in the table Likes like themselves. So you will not see a combination like a|a in Likes, or b|b in Likes. Can anyone help me out with this?
select primary.A, count(*)
from likes primary
inner join likes secondary on primary.B = secondary.A
group by primary.A
Since you need consider only two connections at once, this can be done with joins. (If you had to consider the full closure of the Likes relation, then you would need the full power of recursion, such as an implementation of Dijkstra's algorithm.)
SELECT X.A AS user, COUNT(DISTINCT Y.B) AS countOfUsersWhoAreTwoDegreesFromUser
FROM Likes AS X
INNER JOIN Likes AS Y
ON X.B = Y.A
GROUP BY user
EDIT: To be clear, this problem is simple and reasonable efficient for any fixed degree of separation.y
EDIT 2: Here's a variant solution which will prevent a user from being counted as two degrees from themselves. This varies from the literal problem description, but might be what was intended.
SELECT X.A AS user, COUNT(DISTINCT Y.B) AS countOfUsersWhoAreTwoDegreesFromUser
FROM Likes AS X
INNER JOIN Likes AS Y
ON X.B = Y.A
WHERE X.A <> Y.B
GROUP BY user
NOTE: this approach is not Scalable ...
If you are interested ONLY and ONLY in two degrees, we can go for a self join ...
Condition T1.A <> T2.B is to filter out A liking A, Distinct is applied so that even if A like C by two degrees by two different path, its still counted as 1.
SELECT T.A, Count(T.B)
FROM
(
SELECT DISTINCT T1.A, T2.B
FROM Table1 T1
INNER JOIN Table1 T2 on T1.B = T2.A AND T1.A <> T2.B
) T
GROUP BY T.A
To cope with an arbitrary value for degree, a CTE could be used in PostgreSQL:
with recursive graph (a, b, path, degree) as
(
select a, b, array[a::text, b::text] as path, 1 as degree
from likes
union all
select l.a, l.b, g.path || l.b::text, g.degree + 1
from likes l
join graph g on l.a = g.b and l.b g.a
)
select *
from graph
where degree = 2