Loop through table records - mysql

I have a table with a list of names in it as below
+----------+
| CLI_NAME |
+----------+
| A |
| B |
| C |
| D |
| E |
+----------+
I'd like to loop through this something like
for each value in CLI_NAME do
{
bla bla
}
For each value in the table CLI_NAMES, I'd like to loop through and execute the below.
The value selected from the table above needs to go in as a parameter to T_NAME such as T_NAME = "abc" in the query below
INSERT INTO TABLE_2(DATE, O_WT)
SELECT
F_DATE,
CASE
WHEN
SUM(
CASE
WHEN
F_NAME like 'ty%asd%'
THEN
F_PKEY
END
) = 1 THEN 1
WHEN
SUM(
CASE
WHEN
F_NAME like 'ty%asd%'
THEN
F_PKEY
END
) = 2 THEN 2
WHEN
SUM(
CASE
WHEN
F_NAME like 'ty%asd%'
THEN
F_PKEY
END
) BETWEEN 3 AND 5 THEN 3
WHEN
SUM(
CASE
WHEN
F_NAME like 'ty%asd%'
THEN
F_PKEY
END
) > 5 THEN 5
ELSE 0
END AS O_WT
FROM
TABLE_1
WHERE
F_NAME IN (
SELECT I_NAME from I_WD WHERE I_I_ID IN (
SELECT I_MAP_ID FROM T_T_MAP where T_MAP_ID = (
SELECT T_ID FROM TWD WHERE T_NAME = 'abc'
)
)
)
AND F_DATE between '2015-05-01' and '2015-06-01'
AND F_NAME LIKE 'ty%pr%'
GROUP BY F_DATE
How could I loop through & do this please?

If I understand correctly, you can do what you want using CROSS JOIN:
INSERT INTO TABLE_2(DATE, O_WT)
SELECT t1.F_DATE,
(CASE WHEN SUM(CASE WHEN t1.F_NAME like 'ty%asd%' THEN t1.F_PKEY END) = 1 THEN 1
WHEN SUM(CASE WHEN t1.F_NAME like 'ty%asd%' THEN t1.F_PKEY END) = 2 THEN 2
WHEN SUM(CASE WHEN t1.F_NAME like 'ty%asd%' THEN t1.F_PKEY END) BETWEEN 3 AND 5 THEN 3
WHEN SUM(CASE WHEN t1. F_NAME like 'ty%asd%' THEN t1.F_PKEY END) > 5 THEN 5
ELSE 0
END)
END AS O_WT
FROM TABLE_1 t1 CROSS JOIN
CLI_NAMES
WHERE t1.F_NAME IN (SELECT I_NAME
FROM I_WD
WHERE I_I_ID IN (SELECT I_MAP_ID
FROM T_T_MAP
WHERE T_MAP_ID = (SELECT T_ID
FROM TWD
WHERE T_NAME = 'abc'
)
)
) AND
t1.F_DATE between '2015-05-01' and '2015-06-01' AND
t1.F_NAME LIKE 'ty%pr%'
GROUP BY t1.F_DATE

So you want 'abc' in WHERE T_NAME = 'abc' replaced with the CLI_NAME?
Does this suffice:
select i_map_id from t_t_map where t_map_id in
(
select t_id from twd where t_name in (select cli_name from cli_names)
)
instead of
select i_map_id from t_t_map where t_map_id =
(
select t_id from twd where t_name = 'abc'
)
It would not quite be the same as a loop. E.g. if 'A' and 'B' lead to the same t_ids, then you'd get the inserts just once instead of twice.

Related

MySQL Query: How can I select rows which more times recommended then not recommended

Table: tbl_1
Columns: name, option
-> name | option
-> abc_1 | yes
-> abc_2 | no
-> abc_3 | yes
-> abc_1 | no
-> abc_2 | yes
-> abc_3 | no
-> abc_1 | yes
-> abc_2 | no
-> abc_3 | yes
-> abc_1 | yes
-> abc_2 | no
-> abc_3 | yes
Now, The query I want to ask that ......
How can I get those unique names which are have options:yes more than no ?
The query should return
-> name
-> abc_1
-> abc_3
I tried lots of queries but did not succeed, Kindly help
Thanks.
MySql evaluates Boolean expressions as 0 or 1 for False and True, so there is no need for IF or CASE statements:
select name
from tbl_1
group by name
having sum(`option` = 'yes') > sum(`option` = 'no')
See the demo.
Results:
| name |
| ----- |
| abc_1 |
| abc_3 |
A terse MySQL query to return the specfied result would be something like:
SELECT t.name
FROM tbl_1 t
GROUP
BY t.name
HAVING SUM(IF(t.option = 'yes',1,0)) > SUM(IF(t.option = 'no',1,0))
This is using some "conditional aggregation" shorthand.
Let's unpack that a bit, so we can get an understanding of what this query is doing. Consider this query:
SELECT t.name
, t.option
, IF(t.option = 'yes',1,0) AS option_yes
, IF(t.option = 'no' ,1,0) AS option_no
FROM tbl_1 t
ORDER
BY t.name
The expressions in the SELECT list are evaluated for each row, the IF() function evaluates to either a 1 or a 0.
And now consider what happens when do aggregation of those expressions, adding up all of the 1s and 0s
SELECT t.name
, t.option
, SUM( IF(t.option = 'yes',1,0) ) AS cnt_option_yes
, SUM( IF(t.option = 'no' ,1,0) ) AS cnt_option_no
FROM tbl_1 t
GROUP
BY t.name
, t.option
Now leave "option" out of the group by clause, and do a comparison of the two aggregates.
SELECT t.name
, SUM( IF(t.option = 'yes',1,0) ) AS cnt_option_yes
, SUM( IF(t.option = 'no' ,1,0) ) AS cnt_option_no
, SUM(IF(t.option='yes',1,0) ) > SUM(IF(t.option='no',1,0)) AS _compare
FROM tbl_1 t
GROUP
BY t.name
It's possible to do a comparison of the aggregates in a HAVING clause. And that yields the query at the very beginning of this answer.
You could try a conditional sum
select name
, sum( case when `option` ='yes' then 1 else 0 end) num_yes
, sum( case when `option` ='no' then 1 else 0 end) num_no
from my_table
group by name
order by num_yes desc, num_no desc
Conditional aggregation -
select
name,
count (case when `option` = 'yes' then 1 else null end) as y,
count (case when `option` <> 'yes' then 1 else null end) as n
from
tbl_1
group by
`name`
having count (case when `option` = 'yes' then 1 else null end) > count (case when `option` <> 'yes' then 1 else null end)
Fiddle

How to select values by groups, using select sum()?

I need to select values by groups (from 0 to 10, from 10 to 50, more than 50).
user_id amount
1 20
1 40
2 5
3 30
3 1
Why this query doesn't work correctly?
select (select sum(amount)),
case
when (select sum(amount))<10 then '0-10'
when (select sum(amount))>=10 and (select sum(amount))<50 then '10-20'
else '>50' end as total_amount, count(distinct user_id)
from table
group by
case
when (select sum(amount))<10 then '0-10'
when (select sum(amount))>=10 and (select sum(amount))<50 then '10-20'
else '>50' end as total_amount, count(distinct user_id);
output
diapason number_of_users
0-10 1
10-50 1
>50 1
Give me hint plz
Your query has a number of issues, but primarily it will not work because you need to do the sum by user prior to the sorting into ranges. Try this instead:
SELECT CASE
WHEN amount BETWEEN 0 AND 9 THEN ' 0-10'
WHEN amount BETWEEN 10 AND 50 THEN '10-50'
ELSE '>50' END AS diapason,
COUNT(*) AS number_of_users
FROM (SELECT SUM(amount) AS amount
FROM payments
GROUP BY user_id) p
GROUP BY diapason;
Output
diapason number_of_users
0-10 1
10-50 1
>50 1
Try below way
select
sum(case when amount <10 then 1 else 0 end) as "0-10",
sum(case when amount >=10 and amount <50 then 1 else 0 end) as "0-50" , sum(case when amount>50 then 1 else 0 end) as ">50"
from table
Your syntax is incorrect:
select case
when amount<10 then '0-10'
when samount>=10 and amount<50 then '10-20'
else '>50' end as total_amount, count(distinct user_id)
from table
group by
case
when amount<10 then '0-10'
when samount>=10 and amount<50 then '10-20'
else '>50' end
You can try this, writting a subquery get SUM of amount by each user_id, then do CASE WHEN, you don't need select sum(amount) in CASE WHEN.
CREATE TABLE t(
user_id int,
amount int
);
insert into t values(1,20);
insert into t values(1,40);
insert into t values(2,5);
insert into t values(3,30);
insert into t values(3,1);
Query 1:
select
case
when t1.total<10 then '0-10'
when t1.total>=10 and t1.total<50 then '10-50'
else '>50' end as diapason,
count(distinct user_id) number_of_users
from (
SELECT user_id,SUM(amount) total
FROM table
GROUP BY user_id
) t1
group by
case
when t1.total<10 then '0-10'
when t1.total>=10 and t1.total<50 then '10-50'
else '>50' end
Results:
| diapason | number_of_users |
|----------|-----------------|
| 0-10 | 1 |
| 10-50 | 1 |
| >50 | 1 |

Mysql distinct sum, group by

This is my table:
id user_id type value
1 1 type1 2
2 2 type1 1
3 1 type2 5
4 1 type1 2
I want output like this:
user_id type1 type2
1 4 5
2 1 0
please help
You can try like this:
SELECT
user_id,
sum( if( type = 'type1', value, 0 ) ) AS type1,
sum( if( type = 'type2', value, 0 ) ) AS type2
FROM
table_name
GROUP BY
user_id;
I think, this is what you are searching for:
select
user_id,
(select sum(value) from <yourtablename> sub1 where type = 'type1' where sub1.user_id = baseTable.user_id) as type1,
(select sum(value) from <yourtablename> sub2 where type = 'type2' where sub2.user_id = baseTable.user_id) as type2,
from
<yourtablename> baseTable

If not found in record then get other where case in mysql

Need to check in where case that if not found where type='P' then it take record of type='C'
here is table
paper_id | product_id | type
1 1 P
2 1 P
3 1 C
4 1 C
5 2 C
6 2 C
There is product_id 1 and 2, need to get those record that have type='P' but those product who have not type='P' the record get from record type='C'
after query need this result
paper_id | product_id | type
1 1 P
2 1 P
5 2 C
6 2 C
i try
select * from table where CASE WHEN type !='P' THEN type='C' ELSE type='P'END
but not working
select paper_id, product_id, type from your_tab
where type = 'P'
union all
select t1.paper_id, t1.product_id, t1.type from your_tab t1
where t1.type = 'C'
and not exists (select 1 from your_tab t2
where t2.product_id = t1.product_id and t2.type = 'P');
maybe this will help
SELECT DISTINCT T.PRODUCT_ID, T.PAPER_ID, T.TYPE
FROM YOUR_TABLE T
WHERE (CASE
WHEN T.TYPE = 'P' THEN
'TRUE'
WHEN T.TYPE != 'P' THEN
(CASE
WHEN (SELECT COUNT(*)
FROM YOUR_TABLE T2
WHERE T2.PRODUCT_ID = T.PRODUCT_ID
AND T2.TYPE = 'P') = 0 THEN
'TRUE'
ELSE
'FALSE'
END)
END) = 'TRUE'

SQL query - print the min and max from one table in the same row

I need a little help with an sql query.
I have a table with a format and data that looks like this:
id | applicant_id | application_status | status_time
1 | 1234 | received | 2013-05-06 15:00:00
1 | 1234 | pending | 2013-05-06 15:30:00
1 | 1234 | approved | 2013-05-06 16:00:00
The problem that I need to solve will have to print the following:
applicant_id | initial_status | initial_time | current_status | current_status_time
1234 | received | 2013-05-06 15:00:00 | approved | 2013-05-06 16:00:00
How could I go about accomplishing something like this, preferably only using joins and no nested selects?
The best way to approach this, in general, is to use the row_number() function. However, this requires a nested select:
select t.applicant_id,
max(case when seqnum_asc = 1 then status end) as initial_status,
max(case when seqnum_asc = 1 then status_time end) as initial_time,
max(case when seqnum_desc = 1 then status end) as current_status,
max(case when seqnum_desc = 1 then status_time end) as current_time
from (select t.*,
row_number() over (partition by applicant_id order by status_time) as seqnum_asc,
row_number() over (partition by applicant_id order by status_time desc) as seqnum_desc
from t
) t
group by t.applicant_id;
If your database did not support row_number(), I would recommend correlated subqueries, for readability. But those are also nested. Here is a solution in MySQL that meets your requirements:
select t.applicant_id,
substring_index(group_concat(status) separator ',' order by status_time), ',', 1) as initial_status,
min(status_time) as initial_time,
substring_index(group_concat(status) separator ',' order by status_time desc), ',', 1) as current_status,
max(status_time) as current_time
from t
group by t.applicant_id;
You did not state your database product, but you could use something like this on any database:
select t1.id,
t1.applicant_id,
max(case when t1.status_time = t2.mintime then t1.application_status end) initial_status,
max(case when t1.status_time = t2.mintime then t1.status_time end)initial_time,
max(case when t1.status_time = t2.maxTime then t1.application_status end) current_status,
max(case when t1.status_time = t2.maxTime then t1.status_time end) `current_time`
from yourtable t1
inner join
(
select id, applicant_id,
max(status_time) maxTime,
min(status_time) mintime
from yourtable
group by id, applicant_id
) t2
on t1.id = t2.id
and t1.applicant_id = t2.applicant_id
and
(
t1.status_time = t2.mintime
or t1.status_time = t2.maxtime
)
group by t1.id, t1.applicant_id;
See SQL Fiddle with Demo
SELECT a.application_id
, a.application_status as initial_status
, a.status_time as initial_time
, b.application_status as current_status
, b.status_time as current_status_time
FROM sample1 A
CROSS JOIN sample1 B
WHERE A.application_status = 'received'
and b. application_status = 'approved'
Assuming MS SQL (Transact-SQL), and that your source table is aptly named [SourceTable]. =)
SELECT DISTINCT
[Probe].applicant_id,
[LogMin].application_status [initial_status],
[LogMin].status_time [initial_time],
[LogMax].application_status [current_status],
[LogMax].status_time [current_status_time]
FROM (
SELECT MAX(status_time) [MaxDate],
MIN(status_time) [MinDate],
[applicant_id]
FROM [SourceTable]
GROUP BY [applicant_id]
) [Probe]
INNER JOIN [SourceTable] [LogMax]
ON [Probe].[applicant_id] = [LogMax].[applicant_id]
AND [Probe].[MaxDate] = [LogMax].[status_time]
INNER JOIN [SourceTable] [LogMin]
ON [Probe].[applicant_id] = [LogMin].[applicant_id]
AND [Probe].[MinDate] = [LogMin].[status_time]
Link to the SQLFiddle test is here.
Assuming that for one applicant_id you have one line for 'received' status and also one line for 'approved' status (as you listed in the question) you can use inline views to solve your issue:
select section1.applicant_id AS applicant_id, 'received' AS initial_status,
section1.status_time AS initial_time, 'approved' AS current_status,
section2.status_time AS current_status_time from
(select applicant_id, status_time from yourtable where application_status = 'received') section1,
(select applicant_id, status_time from yourtable where application_status = 'approved') section2
where section1.applicant_id = section2.applicant_id;
Try something like this.
select
t1.applicant_id,
t2.application_status initial_status,
t1.initial_time,
t3.application_status current_status,
t1.current_status_time
from
(select
applicant_id,
min(status_time) initial_time,
max(status_time) current_status_time
from
your_table
group by
applicant_id) t1
inner join your_table t2
on (t1.applicant_id = t2.applicant_id and t1.initial_time = t2.status_time)
inner join your_table t3
on (t1.applicant_id = t3.applicant_id and t1.current_status_time = t3.status_time)