mysql select distinct if condition exists - mysql

I have this table :
std_id std_type std_dept target
1 type-1 ALL 15
2 type-1 HRD 10
3 type-2 ALL 1
4 type-2 ACCTG 5
5 type-3 ALL 5
6 type-4 ALL 25
std_dept with value ALL means std_target for each std_type are valid for all dept
std_dept with specific dept value, override the value form point (1).
So, my problem is lets say I am from HRD dept and I want to have the result set as follow:
std_id std_type std_dept target
2 type-1 HRD 10
3 type-2 ALL 1
5 type-3 ALL 5
6 type-4 ALL 25
I really like to show my work, but I've got no clue at all, I don't know how to distinctly select * from std_table (for each std_type) where std_dept='HRD' if exist else get from std_dept='ALL'.
I need pointers if not the code

You could use NOT EXISTS and correlated subquery:
SELECT *
FROM tab
WHERE std_dept = 'HRD'
UNION ALL
SELECT *
FROM tab t1
WHERE std_dept = 'ALL'
AND NOT EXISTS (SELECT *
FROM tab t2
WHERE t2.std_dept = 'HRD'
AND t1.std_type = t2.std_type);
DBFiddle Demo

It sounds like you want to weight it, so if you're from HRD then you want HRD or ALL, and if you want ACCTG then you want ACCTG or ALL, for types 1-4.
You could group by std_type to make them distinct, and order by that to make the order correct. For the weighting thing, you could create a column which is the result of a comparison against the same value you're using in your WHERE clause, e.g. something like:
SELECT std_id, DISTINCT(std_type) AS std_type,
std_dept, target,
IF (std_dept = 'HRD', 50, 10) AS weight
FROM mytable
WHERE std_dept IN ('ALL', 'HRD')
GROUP BY std_id, std_dept, target, weight
ORDER BY std_type

Related

MySQL (version lower then 8.0) : Select where date value and return a row of dates from table of milestone date

Table a (milestone) :
Pk_id
emp_id
milestone_date
1
2
2022-01-17
2
2
2021-03-23
3
2
2018-06-29
4
3
2018-01-15
5
3
2016-02-17
...
...
...
Table b :
PK_id
Emp_id
ins_date
1
2
2022-01-20
2
2
2019-03-30
3
3
2017-06-29
My problem is I want to select the floor date row of Table A.but I don't know how to use the command.
The method is, in each row of Table B. Determine the column of ins_date and emp_id. Then use that value to select the date in Table A that is the floor date (I don't know how to explain it. But try to understand in the example below). and then show the results in the list.
for example
Row 1: Table B. The ins_date value is '2022-01-20' and emp_id is 2. Consider emp_id = 2 and the floor date of '2022-01-20' (Because Greater than 2022-01-20 ), so select the item PK_ID = 1.
Row 2: Table B. The ins_date value is '2019-03-30' and emp_id is 2. Consider emp_id = 2 and the floor date of '2018-06-29' (Because Greater than 2018-06-29 but less than 2021-03-23 ), so select item PK_ID = 3.
Row 3: Table B. The ins_date value is '2017-06-29' and emp_id is 3. Consider emp_id = 3 and the floor date of '2016-02-17' (Because Greater than 2016-02-17 but less than 2018-01-15 ), so select the item PK_ID = 5.
Thank you and Sorry my English.
P.S. I don't know how I can explain the question and details. But if you edit the text to make it easier to understand. I strongly allow editing.
You are looking for the maximum milestone_date that is less or equal to an ins_date.
Two ways to get that date:
option #1
select b.*, max(a.milestone_date) as floor_milestone_date
from b
join a on a.emp_id = b.emp_id and a.milestone_date <= b.ins_date
group by b.pk_id
order by b.pk_id;
option #2
select
b.*,
(
select max(a.milestone_date)
from a
where a.emp_id = b.emp_id and a.milestone_date <= b.ins_date
) as floor_milestone_date
from b;
The difference between the two queries above: When there is no floor milestone date for an ins_date, then the first query doesn't return the b row, while the second query returns the b row with a null milestone date.
Now, if you want more information from table a, then join the table to one of the queries above.
The final query
select ab.*, a.pk_id
from
(
select b.*, max(a.milestone_date) as floor_milestone_date
from b
join a on a.emp_id = b.emp_id and a.milestone_date <= b.ins_date
group by b.pk_id
) ab
join a on a.emp_id = ab.emp_id and a.milestone_date = ab.floor_milestone_date
order by ab.pk_id;

MySQL filter/find common value in column

Borrower_id Branch_id
7 1
8 1
8 2
9 1
9 2
With the table above, I am trying to find a query that gives me the common branch_id for borrower_id values. I do not know the values of branch_id but I know that the numbers 7, 8 and 9 have the value 1 as common values whereas the numbers 8 and 9 have the common value 2 (which I don't want to select because 7 has to be included, again, because of the common value 1).
This is the expected output :
Borrower_id Branch_id
7 1
8 1
9 1
Is there a way I can achieve this? Any suggestion will be appreciated.
How about this? Tested with you current set of data but need to be tested in different scenarios you have.
SELECT a.* FROM your_table a
INNER JOIN
(
SELECT branch_id, COUNT(branch_id) cnt
FROM your_table
GROUP BY branch_id
HAVING cnt = (SELECT COUNT(DISTINCT borrower_id) FROM your_table)
) b ON b.branch_id = a.branch_id

Assign rows based on number of "chances" a user has

I need to assign jobs to users based on a score (number of "chances") calculated from previous jobs they have done. Here's my table of users:
user chances
Anna 6
Barry 4
Steve 3
Jackson 3
Helga 3
Maureen 3
Paul 3
Karen 2
Anita 2
Samson 2
Frank 2
Jean 1
Lilly 1
Boris 1
In another table, I have 100 rows of unassigned jobs (with currently NULL user), e.g.
id title user
1 Sort filing NULL
2 Clean office NULL
3 Order stationery NULL
I want to assign these jobs to the users above using a weighting based on the number of "chances" they have. For example, Anna will have 6 chances to be assigned one of these jobs, while Boris will have 1.
I've been playing around with a CASE which will assign a user to jobs, but nothing is satisfactory.
What's the best way for me to achieve this? Thanks
Presumably, you're after something like this...
SELECT user
FROM my_table
ORDER BY RAND() * chances * (SELECT SUM(chances) FROM my_table) DESC ;
If the changes are a small number and integers, then the easiest way might be:
update anothertable at
set user = (select user
from chances c cross join
(select 1 as n union all select 2 union all select 3 union all
select 4 union all select 5 union all select 6
) n
on c.chances <= n.n
where at.user is null
order by rand()
limit 1
);
The where clause is just so MySQL doesn't get the (brilliant) idea of optimizing away the subquery and only calling it once.

MySQL Get rows that do not match with certain conditions

It seems simple in my head but I am at a loss for getting the results I need.
My table
id, code, type
1 1111 1
2 1111 2
3 1222 1 <--- This one
4 1333 1
5 1333 2
6 1444 3 <--- Different type then the others
I want the output of the one that doesn't have a matching code with type 2 but only look for ones with type 1 or type 2 (if that makes sense)
id, code, type
3 1222 1
NOTE: I have over 1 million records to query so I need something fast.
My SqlFiddle
Thanks in advance.
SELECT * FROM codes NATURAL JOIN (
SELECT code
FROM codes
WHERE type IN (1,2)
GROUP BY code
HAVING COUNT(DISTINCT type) = 1
) t
See it on sqlfiddle.
Here is a solution using not exists:
SELECT c.*
FROM codes c
WHERE c.type = 1 and
not exists (select 1
from codes c2
where c2.code = c.code and
c2.type = 2
)

MySQL Group by week num w/ multiple date column

I have a table with columns similar to below , but with about 30 date columns and 500+ records
id | forcast_date | actual_date
1 10/01/2013 12/01/2013
2 03/01/2013 06/01/2013
3 05/01/2013 05/01/2013
4 10/01/2013 09/01/2013
and what I need to do is get a query with output similar to
week_no | count_forcast | count_actual
1 4 6
2 5 7
3 2 1
etc
My query is
SELECT weekofyear(forcast_date) as week_num,
COUNT(forcast_date) AS count_forcast ,
COUNT(actual_date) AS count_actual
FROM
table
GROUP BY
week_num
but what I am getting is the forcast_date counts repeated in each column, i.e.
week_no | count_forcast | count_actual
1 4 4
2 5 5
3 2 2
Can any one please tell me the best way to formulate the query to get what I need??
Thanks
try:
SELECT weekofyear(forcast_date) AS week_forcast,
COUNT(forcast_date) AS count_forcast, t2.count_actual
FROM
t t1 LEFT JOIN (
SELECT weekofyear(actual_date) AS week_actual,
COUNT(forcast_date) AS count_actual
FROM t
GROUP BY weekOfYear(actual_date)
) AS t2 ON weekofyear(forcast_date)=week_actual
GROUP BY
weekofyear(forcast_date), t2.count_actual
sqlFiddle
You have to write about 30 (your date columns) left join, and the requirement is that your first date column shouldn'd have empty week (with a count of 0) or the joins will miss.
Try:
SELECT WeekInYear, ForecastCount, ActualCount
FROM ( SELECT A.WeekInYear, A.ForecastCount, B.ActualCount FROM (
SELECT weekofyear(forecast_date) as WeekInYear,
COUNT(forecast_date) as ForecastCount, 0 as ActualCount
FROM TableWeeks
GROUP BY weekofyear(forecast_date)
) A
INNER JOIN
( SELECT * FROM
(
SELECT weekofyear(forecast_date) as WeekInYear,
0 as ForecastCount, COUNT(actual_date) as ActualCount
FROM TableWeeks
GROUP BY weekofyear(actual_date)
) ActualTable ) B
ON A.WeekInYear = B.WeekInYear)
AllTable
GROUP BY WeekInYear;
Here's my Fiddle Demo
Just in case someone else comes along with the same question:
Instead of trying to use some amazing query, I ended up creating an array of date_columns_names and a loop in the program that was calling this query, and for each date_column_name, performing teh asme query. It is a bit slower, but it does work