Find the lowest value of every row of selected columns - mysql

As the result of the query, I want to get all rows (Drivers), order by the drivers who got most series wins.
If a driver has won 4 tacks at least one or more times but failed to win the remaining track at least once, his series count is 0.
Driver Table
ID|Name| .........
1 A
2 B
3 C
4 D
Tracks Table
TID |FK|Track1_Wins|Track2_Wins| Track3_Wins|Track4_Wins|Track5_Wins|
1 1 5 6 3 2 4
2 2 2 4 0 5 3
3 3 6 3 9 4 7
4 4 5 8 2 4 1
My code sample
SELECT `Drivers`.`Name`, LEAST(`Track1_Wins`, `Track2_Wins`, `Track3_Wins`, `Track4_Wins`, `TRACK5_Wins`) AS Series
FROM `Drivers`, `Tracks`
ORDER BY Series DESC;
Accidently I got part expected output when I use WHERE with Driver ID
SELECT `Drivers`.`Name`, LEAST(`Track1_Wins`, `Track2_Wins`, `Track3_Wins`, `Track4_Wins`, `TRACK5_Wins`) AS Series FROM `Drivers`, `Tracks` WHERE `Drivers`.`ID` = 2 ORDER BY Series DESC;
It will give the expected result but with Same Driver Name as expected
B 3
B 2
B 1
B 0
My expected output is
Name | Series
C 3
A 2
D 1
B 0

Run this,
SELECT d.`Name`,
LEAST(`Track1_Wins`, `Track2_Wins`, `Track3_Wins`, `Track4_Wins`, `TRACK5_Wins`) AS Series
FROM `Drivers` d INNER JOIN `Tracks` t
ON t.`FK` = d.`ID`
ORDER BY Series DESC;
This returns the user name associated with the FK. Also, try to use kebab_case and lower case for all your column and table name. Makes it much easier to run the code

Related

Commonality in many to many relationship SQL query

If I have agents that can have many properties, and vice versa, defined in a in a junction table, how would I query the top agents (an agent who has at least two properties in common with two other agents.)
agent
id
1
2
3
4
5
properties
id
1
2
3
4
5
6
agent_properties
agent_id
property_id
1
1
1
2
1
3
2
2
2
3
3
1
3
3
3
5
4
3
4
4
4
6
5
1
5
2
5
5
6
4
6
6
Example:
Agent 1 (propr1, propr2, propr3),
Agent 2 (propr2, propr3),
Agent 3 (propr1, propr3, propr5)
Agent 4 (propr3, propr4, propr6)
Agent 5 (propr1, propr2, propr5)
Agent 6 (propr4, propr6)
So the query should return Agent 1, Agent 3, Agent 5
If anyone can show how to do this using Laravel Eloquent that would be great.
A solution would be to work with bits.
First you should add a new column in table properties and this new column to be named bin_value. The values from this column must be unique per property and to be power of 2. In your case would be:
id
bin_value
1
1
2
2
3
4
4
8
5
16
6
32
Second, add a new column to agent table named properties and run following update query:
UPDATE agent AS a
INNER JOIN (
SELECT ap.agent_id, SUM(p.bin_value) AS bin_properties
FROM agent_properties AS ap
INNER JOIN properties AS p ON p.id = ap.property_id
GROUP BY ap.agent_id
) AS b ON a.id = b.agent_id
SET a.properties = b.bin_properties
;
Third, run the query with bit function which helps you to find agents which have at least 2 shared properties with at least 2 other agents:
SELECT a1.agent_id
, a1.name
, COUNT(*) AS total_other_agents
FROM agent AS a1
INNER JOIN agent AS a2 ON a1.id != a2.id
AND BIT_COUNT(a1.properties & a2.properties) >= 2 -- at least 2 shared properties
GROUP BY a1.id
HAVING total_other_agents >= 2 -- at least 2 other agents
ORDER BY total_other_agents DESC, a1.name
;
If you have a large number of properties try to set values in bin_value like this:
UPDATE properties
SET bin_value = BINARY(POWER(2, id-1))
;

SQL - Max value from a group by when creating a new field

I have a database with a table called BOOKINGS containing the following values
main-id place-id start-date end-date
1 1 2018-8-1 2018-8-8
2 2 2018-6-6 2018-6-9
3 3 2018-5-5 2018-5-8
4 4 2018-4-4 2018-4-5
5 5 2018-3-3 2018-3-10
5 1 2018-1-1 2018-1-6
4 2 2018-2-1 2018-2-10
3 3 2018-3-1 2018-3-28
2 4 2018-4-1 2018-4-6
1 5 2018-5-1 2018-5-15
1 3 2018-6-1 2018-8-8
1 4 2018-7-1 2018-7-6
1 1 2018-8-1 2018-8-18
1 2 2018-9-1 2018-9-3
1 5 2018-10-1 2018-10-6
2 5 2018-11-1 2018-11-5
2 3 2018-12-1 2018-12-25
2 2 2018-2-2 2018-2-19
2 4 2018-4-4 2018-4-9
2 1 2018-5-5 2018-5-23
What I need to do is for each main-id I need to find the largest total number of days for every place-id. Basically, I need to determine where each main-id has spend the most time.
This information must then be put into a view, so unfortunately I can't use temporary tables.
The query that gets me the closest is
CREATE VIEW `MOSTTIME` (`main-id`,`place-id`,`total`) AS
SELECT `BOOKINGS`.`main-id`, `BOOKINGS`.`place-id`, SUM(DATEDIFF(`end-date`, `begin-date`)) AS `total`
FROM `BOOKINGS`
GROUP BY `BOOKINGS`.`main-id`,`RESERVATION`.`place-id`
Which yields:
main-id place-id total
1 1 24
1 2 18
1 5 5
2 1 2
2 2 20
2 4 9
3 1 68
3 2 24
3 3 30
4 1 5
4 2 10
4 4 1
5 1 19
5 2 4
5 5 7
What I need is then the max total for each distinct main-id:
main-id place-id total
1 1 24
2 2 20
3 1 68
4 2 10
5 1 19
I've dug through a large amount of similar posts that recommend things like self joins; however, due to the fact that I have to create the new field total using an aggregate function (SUM) and another function (DATEDIFF) rather than just querying an existing field, my attempts at implementing those solutions have been unsuccessful.
I am hoping that my query that got me close will only require a small modification to get the correct solution.
Having hyphen character - in column name (which is also minus operator) is a really bad idea. Do consider replacing it with underscore character _.
One possible way is to use Derived Tables. One Derived Table is used to determine the total on a group of main id and place id. Another Derived Table is used to get maximum value out of them based on main id. We can then join back to get only the row corresponding to the maximum value.
CREATE VIEW `MOSTTIME` (`main-id`,`place-id`,`total`) AS
SELECT b1.main_id, b1.place_id, b1.total
FROM
(
SELECT `main-id` AS main_id,
`place-id` AS place_id,
SUM(DATEDIFF(`end-date`, `begin-date`)) AS total
FROM BOOKINGS
GROUP BY main_id, place_id
) AS b1
JOIN
(
SELECT dt.main_id, MAX(dt.total) AS max_total
FROM
(
SELECT `main-id` AS main_id,
`place-id` AS place_id,
SUM(DATEDIFF(`end-date`, `begin-date`)) AS total
FROM BOOKINGS
GROUP BY main_id, place_id
) AS dt
GROUP BY dt.main_id
) AS b2
ON b1.main_id = b2.main_id AND
b1.total = b2.max_total
MySQL 8+ solution would be utilizing the Row_Number() functionality:
CREATE VIEW `MOSTTIME` (`main-id`,`place-id`,`total`) AS
SELECT b.main_id, b.place_id, b.total
FROM
(
SELECT dt.main_id,
dt.place_id,
dt.total
ROW_NUMBER() OVER (PARTITION BY dt.main_id
ORDER BY dt.total DESC) AS row_num
FROM
(
SELECT `main-id` AS main_id,
`place-id` AS place_id,
SUM(DATEDIFF(`end-date`, `begin-date`)) AS total
FROM BOOKINGS
GROUP BY main_id, place_id
) AS dt
GROUP BY dt.main_id
) AS b
WHERE b.row_num = 1

Sum within a column given two or more conditions in MySQL

In MySQL, I am trying to sum values in a column given certain conditions. I have an example of some data below
Team Season Mth Stat
A 1 1 4
A 1 1 4
A 1 2 7
A 1 2 9
B 1 1 6
B 1 1 6
B 1 2 6
B 1 2 9
C 1 1 1
C 1 1 3
C 1 2 3
C 1 2 6
But I need the output to show up as
Team Season Mth Stat
A 1 1 8
A 1 2 16
B 1 1 12
B 1 2 15
C 1 1 4
C 1 2 9
So the Stat column is now the sum of the cells such that Match, Season, and Team are all the same. I have the code below. I see a lot of answers that use 'case' but that seems to be given logical operators that are not equal to each other. When I do it below, now it doesn't recognise the table where the columns are coming from. I do have a inner joins but the data itself is from one table. I get another error as well on the sum function because it requires one argument.
select
Team
,Season
,Match
--this is where I get lost-----------
sum(
select
Stat
From
table
Where
Mth=Mth
AND Season=Season
AND Team=Team
)
--end of getting lost----------------
FROM
table
Where
Season IN (1,2)
GROUP BY
Team
,Season
,Mth
Order BY
Team ASC
Edit:
It turns out I need to use GROUP BY as the comments suggest. So I am not summing within a table, but I sum the variable given the Group By parameters.
Unless I'm missing something, it's simply:
SELECT Team
,Season
,Match
,Sum(Stat)
FROM table
GROUP BY
Team
,Season
,Match
It's simple as this:
SELECT Team,
Season,
Match,
SUM(Stat)
FROM Table
WHERE Season IN (1,2)
GROUP BY Team,
Season,
Match
ORDER BY Team ASC
Please look at the SQL Fiddle example.

Query That Pulls Duplicate Parts On The Same Order

In my system, there should not be the same part# listed on an order more than once. I'm trying to write a query that will pull up any parts that appear on an order more than once. For example:
Order# Part QTY
1 A 1
1 A 1
1 B 5
2 A 4
2 B 4
2 C 3
3 A 5
3 B 5
3 B 7
4 A 3
4 B 6
5 A 3
So the problems here would be Order # 1 because part A appears more than once, and the same thing with Order# 3 because part B appears more than once. The rest of the orders would be fine. Where would I start if I want to achieve something like this.
The following query literally follows what you are asking. It aggregates by the combination of order and part, and returns those values when they occur more than once for a given pair.
SELECT Order, Part
FROM yourTable
GROUP BY Order, Part
HAVING COUNT(*) > 1

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)