How to query rows that are not in a table? - mysql

First of all, I don't think my title is good, but I couldn't think of a better one. Please feel free to change it.
I have a table that keeps record of a pair of rows.
The following is a sample table structure.
table History
user_id row_1 row_2
2 1 2
2 1 3
table Rows
row_id
1
2
3
4
5
6
I would like to query to get only a pair of rows that are not in the 'History' table.
so..I like to get the following result.
row pairs:
1,4
1,5
1,6
2,3
2,4
2,5
2,6
and so on
Can I do it with one query?
Just added:
I just made a query that works, but I am not sure about the performance.
select r1.row_id, r2.row_id from rows as r1 cross join rows as r2
where r1.row_id!=r2.row_id and ( r1.row_id + r2.row_id) not in (select row_1 + row_2 from history)
order by r1.row_id desc
Would it be super slow?

Something like this. You haven't made the correlations clear between the fields but this should be easy to adapt.
select h.row_id r1, r.row_id r2
from rows h
cross join rows r
left join history h2 on h2.row_1=h.row_id and h2.row_2=r.row_id
where h2.row_1 is null
The CROSS JOIN produces all the possible combinations of row_id x row_id
THE LEFT JOIN attempts to find the combination in the history table
The WHERE clause picks out where the combination is not found

I think this might work:
SELECT DISTINCT
CASE WHEN r1.row_id < r2.row_id THEN r1.row_id ELSE r2.row_id END AS row_id_1,
CASE WHEN r1.row_id < r2.row_id THEN r2.row_id ELSE r1.row_id END AS row_id_2
FROM Rows AS r1
INNER JOIN Rows AS r2 /* ON r1.row_id <> r2.row_id */
WHERE (r1.row_id, r2.row_id) NOT IN (
SELECT row_1, row_2
FROM history
UNION
SELECT row_2, row_1
FROM history
)
ORDER BY 1, 2
Returns:
1, 1
1, 4
1, 5
1, 6
2, 2
2, 3
2, 4
2, 5
2, 6
3, 3
3, 4
3, 5
3, 6
4, 4
4, 5
4, 6
5, 5
5, 6
6, 6
This query will be super slow.

Related

Compare 2 views with different number of columns

I have 2 views with different number of columns. 1 of the views has been joined with another view that is why it has additional columns.
The first view has 113 records (View 2), while the updated view (View 1) has 130 columns. I would like to find out the number of records that are extra in View 1
.
View 1 View 2
A|B|C|D|E A|B|C
1 2 3 4 5 1 2 3
1 2 3 7 8
3 2 1 4 5 3 2 1
3 2 1 7 8
expected result :
1 2 3 7 8
3 2 1 7 8
Thanks.
You can get that extra records by using 'not in' or 'not exists' conditions
select * from view1 m where not exists (
select 1 from view2 u where (m.a=u.a and m.b=u.b and m.c=u.c)
You can change those conditions as per your requirement
With left join also will get the required result
select m.* from view1 m left join view2 u
(m.a=u.a and m.b=u.b and m.c=u.c)
where u.a is null and u.b is null and u.c is null
You shoul probably refactor your DB schema and data logic.
But just to resolve your weird requirements you can:
http://sqlfiddle.com/#!9/cf2c50/2
SELECT t.a, t.b, t.c, t.d, t.e
FROM (
SELECT v1.*, IF(#idx = concat(v1.a,v1.b,v1.c),1,0) `filter`,#idx := concat(v1.a,v1.b,v1.c)
FROM v1
INNER JOIN v2
ON v1.a=v2.a AND v1.b=v2.b AND v1.c=v2.c
ORDER BY v1.a,v1.b,v1.c
) t
WHERE t.`filter`=1;
It is not best example of query performance, but it should return expected result.

Show Items even if 0 rows with group by

I'm trying to get the query below to show for each item for each store the amount of each of 4 items we have.
It works great, and I created the temporary table to try to increase speed but my problem is that if the table has no rows for a certain product that product does not show up at all.
I'd like to show all four products(prodNo) regardless of if there is actually any of rows for that specific store.
I researched this site and could not find something similar enough for me to figure it out.
CREATE TEMPORARY TABLE IF NOT EXISTS temp_invoice_dates AS
(
SELECT Invoice_detail.del_date,invoice_Detail.StoreNo,mast_stores.SDesc, invoice_Detail.ProdNo,sold_qty,retn_price,retn_qty,sold_price FROM Invoice_detail
LEFT JOIN mast_stores on invoice_detail.StoreNO=mast_stores.Snum
LEFT JOIN invoice on invoice_detail.Del_Date=invoice.Del_Date and invoice_detail.Invoice_No=invoice.Invoice_No
WHERE Cnum IN ('200','210') AND invoice_detail.Del_Date >= "2016-03-01" AND invoice_detail.Del_Date < "2016-04-01"
);
SELECT
temp_invoice_dates.StoreNo,
temp_invoice_dates.SDesc,
DATE_FORMAT(temp_invoice_dates.Del_Date,'%Y') as Year,
DATE_FORMAT(temp_invoice_dates.Del_Date,'%M') as Month,
temp_invoice_dates.ProdNo,
mast_items.IDesc,
SUM(sold_qty) as TotalIn,
SUM(retn_qty) as TotalOut,
ROUND(SUM((sold_qty*sold_price)-(retn_qty*retn_price)),2) as NetSales,
CONCAT(ROUND(SUM(retn_qty)/SUM(sold_qty),2)*100,'%') as StalePerc
FROM mast_Items
LEFT JOIN temp_invoice_dates on temp_invoice_dates.ProdNo=mast_items.Inum
WHERE mast_items.Inum in ('3502','3512','4162','4182')
GROUP BY temp_invoice_dates.StoreNo, ProdNo
ORDER BY temp_invoice_dates.StoreNo, ProdNo;
Drop table temp_invoice_dates;
Results are similar to:
StoreNo Product Count....
1 1 1
1 2 5
1 3 2
1 4 1
2 1 14
2 2 1
2 4 4
3 2 33
3 3 3
Where as I'd like it to be
StoreNo Product Count ....
1 1 1
1 2 5
1 3 2
1 4 1
2 1 14
2 2 1
2 3 0
2 4 4
3 1 0
3 2 33
3 3 3
3 4 0
Something like this should work.
SELECT sp.StoreNo, sp.ProdNo
, ...stuff...
, sp.IDesc, sp.SDesc
, ...more stuff...
FROM (
SELECT i.Inum AS ProdNo, s.Snum AS StoreNo
, i.IDesc, s.SDesc
FROM mast_Items AS i, mast_stores AS s
WHERE i.Inum IN ('3502','3512','4162','4182')
) AS sp
LEFT JOIN temp_invoice_dates AS tid
ON sp.ProdNo = tid.ProdNo
AND sp.StoreNo = tid.StoreNo
GROUP BY sp.StoreNo, sp.ProdNo
ORDER BY sp.StoreNo, sp.ProdNo
;
Normally I recommend against cross joins (as seen in the subquery) but in this case it is exactly what is needed. If the query is slow, you can instead insert the subquery results into a temp table beforehand, index that, and then use the temp table in place of the subquery.
(Edit: should use sp fields when available for grouping and results)

MySQL UPDATE with GROUP and ORDER

I'm trying to make an update on a table so that it can increment the values on 1 column depending on another's order.
Here's how it'd go
ID GROUP_ID ORDER(Desired) ORDER(NOW)
1 1 1 2
2 1 2 3
3 1 3 1
4 2 1 2
5 2 2 1
6 3 1 1
7 3 2 1
8 3 3 2
So what I need is for each ID, to update the ORDER column so it can be consecutive, starting from 1, within each GROUP_ID.
I have found some solutions to similar problems regarding the updates and orders, but none that uses multiple orders for groups within the same table.
Hope I illustrated the problem right. Thanks in advance
You can do it by "ranking" the rows over again. Mysql doesn't support window functions but you can achieve the same results with join and count like this:
UPDATE YourTable t
INNER JOIN(SELECT s.id,s.group_id,count(*) as cnt
FROM YourTable s
INNER JOIN YourTable ss
ON(s.group_id = ss.group_id and s.id >= ss.id)
GROUP BY s.id,s.group_id) tt
ON (t.id = tt.id and t.group_id = tt.group_id)
SET t.order = tt.cnt

MYSQL Left Join COUNTS AND SUMS from multiple tables

I have tried looking for similar questions here in SO and have not found any so far. Feel free to tag the duplicate or similar question to this one if you'd like.
I want to join several columns to display a report.
I have 3 tables.
Users
ID Name
1 Chris
2 John
3 Rick
InMessages
ID Content
1 Hello1
2 Hello2
3 Response1
4 Response2
5 Hello3
6 Hello4
OutMessages
ID UserID InMessageID Content CurrentRate
1 1 1 ReplyHello1 10
2 2 2 Reply1Hello2 10
3 3 2 Reply2Hello2 10
4 3 2 Reply3Hello2 10
5 1 3 ReplyResponse1 10
6 2 4 ReplyResponse2 10
7 1 5 ReplyHello3 4
8 3 6 ReplyHello4 4
And the report I'd like to see would be something like:
User InMessagesCount OutMessagesCount Rate10 Rate4
Chris 3 3 2 1
John 2 2 1 1
Rick 2 3 2 1
I have tried the existing queries like:
count(distinct InMessages.ID) as InMessagesCount,
count(distinct OutMessages.ID) as OutMessagesCount
But I am stuck as to how I could make the one for the rates.
Thanks in advance!
You issue seems to be that your multiple joins are creating duplicate records, hence needing DISTINCT in your current counts. But you can't use that for counting the number of occurrences of a non distinct field
As such I would be tempted to try something like this:-
COUNT(DISTINCT IF(OutMessages.CurrentRate = 10, OutMessages.ID, NULL) ) AS Rate10,
COUNT(DISTINCT IF(OutMessages.CurrentRate = 4, OutMessages.ID, NULL) ) AS Rate4
Ie, if the id is 10 then use the id, otherwise use NULL. COUNT like this should (I think) only count the non null values.
For Mysql you can use sum() with expression so it will evaluate as a count that you need for your 2 rates
select
u.Name,
count(distinct o.InMessageID) as InMessagesCount,
count(distinct o.ID) as OutMessagesCount,
sum(o.CurrentRate = 10) rate10,
sum(o.CurrentRate = 4) rate10
from OutMessages o
joins users u on(u.id = o.user_id)
group by u.id
You can sum up those records that match a specific rate:
SUM(IF(OutMessages.CurrentRate = 10, 1,0)) AS Rate10,
SUM(IF(OutMessages.CurrentRate = 4, 1,0)) AS Rate4
You can use IF and SUM, like this:
SELECT UserID,
COUNT(DISTINCT InMessages.ID) AS InMessagesCount,
COUNT(DISTINCT OutMessages.ID) AS OutMessagesCount,
SUM(IF(OutMessages.CurrentRate = 10, 1, 0)) AS Rate10,
SUM(IF(OutMessages.CurrentRate = 4, 1, 0)) AS Rate4
FROM OutMessages
GROUP BY UserID

mysql row numbering reset every different record values

My question is pretty similar to this one Auto number and reset count for each different column value
except that I can't make it work.
I have the table record:
ID(autoINC) plate_number
1 A
2 A
3 A
4 B
5 B
6 C
7 C
I want to display something like this adding additional field cc:
I have the table record:
ID(autoINC) plate_number count
1 A 1
2 A 2
3 A 3
4 B 1
5 B 2
6 C 1
7 C 2
You can have a correlated subquery which sequentially count the row which can be used as a rownumber.
SELECT A.ID,
A.plate_number,
(
SELECT COUNT(*)
FROM tableName c
WHERE c.plate_number = a.plate_number AND
c.ID <= a.ID) AS RowNumber
FROM TableName a
SQLFiddle Demo