How to build this query? - mysql

i have table
http://oi58.tinypic.com/2s7xreo.jpg
id action_date type item_id quantity
--- ----------- ---- ------- --------
87 4/25/2014 1 s-1 100
88 4/1/2014 1 s-1 150
89 4/4/2014 1 s-1 200
90 4/3/2014 1 s-2 222
91 4/7/2014 1 s-2 10
96 4/4/2014 1 s-2 8
97 4/22/2014 1 s-2 8
98 4/21/2014 2 s-1 255
99 4/5/2014 2 s-1 6
100 4/6/2014 2 s-2 190
101 4/6/2014 2 s-3 96
102 4/8/2014 2 s-1 120
103 4/15/2014 2 s-2 3
104 4/16/2014 2 s-2 3
type column which mean if 1 this is in item to my shop >>> if 2 this is out item from my shop >>
i need query to give me result like this
item in out net
s1 300 195 105
and so on >>
how to write query that give me this result >>
and if i must but them in tow table >> table for the in and table for the out if that help >>> how it build the query >
and thanx in advance :)
note :: i am work on access

One way to get the result is to use an aggregate function on an expression that conditionally returns the value of quantity, based on the value of type.
SELECT t.item
, SUM(CASE WHEN t.type = 1 THEN t.quantity ELSE 0 END) AS in_
, SUM(CASE WHEN t.type = 2 THEN t.quantity ELSE 0 END) AS out_
, SUM(CASE WHEN t.type = 1 THEN t.quantity ELSE 0 END)
- SUM(CASE WHEN t.type = 2 THEN t.quantity ELSE 0 END) AS net_
FROM mytable t
GROUP BY t.item
This approach works in Oracle, as well as MySQL and SQL Server.
(If you remove the SUM() aggregate function and the GROUP BY clause, you can see how that CASE expression is working. The query above gives the result you specified, this one is just a demonstration that helps "explain" how that query works.)
SELECT t.item
, CASE WHEN t.type = 1 THEN t.quantity ELSE 0 END AS in_
, CASE WHEN t.type = 2 THEN t.quantity ELSE 0 END AS out_
, t.*
FROM mytable t
UPDATE
Unfortunately, Microsoft Access doesn't support CASE expressions. But Access does have an iif function. The same approach should work, the syntax might be something like this:
SELECT t.item
, SUM(iif(t.type = 1, t.quantity, 0) AS in_
, SUM(iif(t.type = 2, t.quantity, 0) AS out_
, SUM(iif(t.type = 1, t.quantity, 0)
- SUM(iif(t.type = 2, t.quantity, 0) AS net_
FROM mytable t
GROUP BY t.item

SELECT item_id,
In_col,
Out_col,
(In_col - Out_col) AS Net
FROM
(SELECT item_id,
sum(CASE WHEN TYPE = 1 THEN quantity END) AS In_col,
sum(CASE WHEN TYPE = 2 THEN quantity END) AS Out_col
FROM TABLE
GROUP BY item_id) AS t1;
I hope this what you need.

Related

How to use count in sql based on a IF condition

From this table
groupId
flag
flagValue
1
0
500
2
0
100
1
1
10
2
1
50
3
0
100
1
1
200
3
1
1000
2
1
50
I need this result
groupId
flag1
flag0
valFlag1
valFlag0
totalFlags
1
2
1
210
500
3
2
2
1
100
100
3
3
1
1
1000
100
2
where
flag1 is number of times flag is 1 for a particular group
flag0 is number of times flag is 0 for a particular group
valFlag1 is sum of flagVal when flag is 1
valFlag0 is sum of flagVal when flag is 0
totalFlags is sum of total flags associated with a group
I am stuck as to how to actually count values based on an IF condition.
Anyhelp is appreciated. Thanks.
I have used a table named group_table with your values
Try using this:
SELECT
g.`groupId`,
SUM(g.`flag`=1 ) AS flag1,
SUM(g.`flag`=0) AS flag0,
SUM(CASE WHEN g.`flag`=1 THEN g.`flagValue` ELSE 0 END) AS valFalg1,
SUM(CASE WHEN g.`flag`=0 THEN g.`flagValue` ELSE 0 END) AS valFalg0,
COUNT(*) AS totalFlags
FROM
`group_table` g
GROUP BY g.`groupId`
If you have to use the IF,
SELECT
g.`groupId`,
IF(g.`flag`=1,1,0 ) AS flag1,
IF(g.`flag`=0,1,0) AS flag0,
SUM(IF(g.`flag`=1,g.`flagValue`,0 )) AS valFalg1,
SUM(IF(g.`flag`=0,g.`flagValue`,0 )) AS valFalg0,
COUNT(*) AS totalFlags
FROM
`group_table` g
GROUP BY g.`groupId`, flag1, flag0
They'll produce the same result

Unique & Rept Customer Sales based on date

I am new to Mysql, I have the data like this,
Order_dt User_ID Sales
28-03-2022 PRPK-12 84
28-03-2022 PRPK-11 41
28-03-2022 PRPK-10 55
28-03-2022 PRPK-12 76
26-03-2022 PRPK-10 54
27-03-2022 PRPK-11 88
27-03-2022 PRPK-11 51
27-03-2022 PRPK-10 40
27-03-2022 PRPK-10 40
& I need o/p like below the format.
Order_date Unique_Cx Unique_Cx_Sales Rept_Cx Rept_Cx_Sales
26-03-2022 1 54 0 0
27-03-2022 0 0 2 219
28-03-2022 2 96 1 160
I'm getting only unique & Rept count by using the 'Having' function, but im unable to map with the sales. Kindly help.
Thanking you.
First group by dt and user id in sub query then use conditional aggregation
select order_dt,
sum(case when cd = 1 then 1 else 0 end) unique_cx,
sum(case when cd = 1 then sales else 0 end) unique_cx_sales,
sum(case when cd > 1 then 1 else 0 end) repeat_cx,
sum(case when cd > 1 then sales else 0 end) repeat_cx_sales
from
(
select order_dt, user_id,
count(*) cd,sum(sales) sales
from t
group by order_dt,user_id
) s
group by order_dt
;

How to add a null value in the group_concat if the record does not exist?

I have this SQL query with substring_index and group_concat but the results I get does not give the right location of the values because the values does not exist.
I need to add a null or zero value in order to have the right location of the values in the sql result.
In the table there are three lid (1, 2, 3). The lid should be the basis count of the P's (P1, P2, P3) for the substring_index.
This is the table:
lid class_id class total
----- ------- ----- -----
1 73 Leader 10000
1 77 Consultant 8000
1 83 Coordinator 6000
2 73 Leader 20000
2 76 Staff 8000
2 77 Consultant 10000
3 73 Leader 30000
3 78 Team Leader 8000
This is the SQL query I used to group_concat for the totals and substring_index to separate the grouped values with their each column (P1, P2, P3)
SELECT *, GROUP_CONCAT(lid) as lids, GROUP_CONCAT(pyear) as pyears,
COUNT(DISTINCT lib_id) AS total_count,
CASE WHEN COUNT(*)>=1 THEN SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(if(total is null,0,total) ORDER BY lid ASC SEPARATOR ' '),' ',1),' ',-1) END AS P1,
CASE WHEN COUNT(*)>=2 THEN SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(if(total is null,0,total) ORDER BY lid ASC SEPARATOR ' '),' ',2),' ',-1) END AS P2,
CASE WHEN COUNT(*)>=3 THEN SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(if(total is null,0,total) ORDER BY lid ASC SEPARATOR ' '),' ',3),' ',-1) END AS P3
FROM (
SELECT * FROM view_items WHERE lid='1'
UNION
SELECT * FROM view_items WHERE lid='2'
UNION
SELECT * FROM view_items WHERE lid='3'
) AS AZ GROUP BY class_id
This is the result of the above query:
class id class lids P1 P2 P3
--------- ----- ----- ---- ---- ----
73 Leader 1,2,3 10000 20000 30000
76 Staff 2 8000
77 Consultant 1,2 8000 10000
78 Team Leader 3 8000
83 Coordinator 1 6000
The lids should always have three count even though the record does not exists, a zero or null value should be added. How to do the adding of null value?
This is the expected result I need.
class id class lids P1 P2 P3
--------- ----- ----- ---- ---- ----
73 Leader 1,2,3 10000 20000 30000
76 Staff 0,2,0 0 8000 0
77 Consultant 1,2,0 8000 10000 0
78 Team Leader 0,0,3 0 0 8000
83 Coordinator 1,0,0 6000 0 0
To get 0 values where no lid is present in the table, you need to generate a list of all lid values for all class_id values, which you can do with a CROSS JOIN of two SELECT DISTINCT queries (one for lid, one for class_id). This can then be LEFT JOINed to the table, to get the required total value for each P group using conditional aggregation:
SELECT c.class_id,
MAX(v.class),
GROUP_CONCAT(COALESCE(v.lid, 0) ORDER BY l.lid) AS lids,
MAX(CASE WHEN v.lid=1 THEN total ELSE 0 END) AS P1,
MAX(CASE WHEN v.lid=2 THEN total ELSE 0 END) AS P2,
MAX(CASE WHEN v.lid=3 THEN total ELSE 0 END) AS P3
FROM (SELECT DISTINCT lid FROM view_items) l
CROSS JOIN (SELECT DISTINCT class_id FROM view_items) c
LEFT JOIN view_items v ON v.lid = l.lid AND v.class_id = c.class_id
GROUP BY c.class_id
Output:
class_id class lids P1 P2 P3
73 Leader 1,2,3 10000 20000 30000
76 Staff 0,2,0 0 8000 0
77 Consultant 1,2,0 8000 10000 0
78 Team Leader 0,0,3 0 0 8000
83 Coordinator 1,0,0 6000 0 0
Demo on dbfiddle
Use else 0 in case expression
SELECT *, GROUP_CONCAT(lid) as lids, GROUP_CONCAT(pyear) as pyears,
COUNT(DISTINCT lib_id) AS total_count,
CASE WHEN COUNT(*)>=1 THEN SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(if(total is null,0,total) ORDER BY lid ASC SEPARATOR ' '),' ',1),' ',-1) else 0 END AS P1,
CASE WHEN COUNT(*)>=2 THEN SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(if(total is null,0,total) ORDER BY lid ASC SEPARATOR ' '),' ',2),' ',-1) else 0 END AS P2,
CASE WHEN COUNT(*)>=3 THEN SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(if(total is null,0,total) ORDER BY lid ASC SEPARATOR ' '),' ',3),' ',-1) else 0 END AS P3
FROM (
SELECT * FROM view_items WHERE lid='1'
UNION
SELECT * FROM view_items WHERE lid='2'
UNION
SELECT * FROM view_items WHERE lid='3'
) AS AZ GROUP BY class_id
I think the problem is, that you say count >= 1, but count >= 3 is also count >=1 so your code never reaches this line. You have to say,
CASE WHEN COUNT() >=1 AND COUNT () < 2...
CASE WHEN COUNT() >=2 AND COUNT () < 3...
I don't think GROUP_CONCAT() is the right approach for what you want. Try this:
SELECT vi.class_id, vi.class,
COUNT(DISTINCT vi.lib_id) AS total_count,
CONCAT_WS(',',
MAX(CASE WHEN vi.lid = 1 THEN 1 ELSE 0 END),
MAX(CASE WHEN vi.lid = 2 THEN 1 ELSE 0 END),
MAX(CASE WHEN vi.lid = 3 THEN 1 ELSE 0 END)
) as lids,
SUM(CASE WHEN vi.lid = 1 THEN vi.total ELSE 0 END) as total_1,
SUM(CASE WHEN vi.lid = 2 THEN vi.total ELSE 0 END) as total_2,
SUM(CASE WHEN vi.lid = 3 THEN vi.total ELSE 0 END) as total_3
FROM view_items vi
WHERE vi.lid IN (1, 2, 3)
GROUP BY vi.class_id;
Notes:
Your subquery and UNION are not needed. In MySQL, these can actually hurt performance.
I assume that lid is a number, so I've removed the single quotes.
You can use conditional aggregation for each of the totals that you want. Parsing GROUP_CONCAT() is not the right way to do this.
Your question is about the lids. The CONCAT_WS() does what you want -- concatenating either the value (if it appears) or zero if it does not.

generate multiple sums from the same query

I have a table like this:
id person_id total amount date_time
1 101 2000 2001-12-10
2 102 1000 2001-12-10
3 102 3000 2001-12-10
4 102 2000 2001-12-10
5 103 1000 2001-12-11
6 101 1000 2001-12-11
7 102 3000 2001-12-11
8 102 4000 2001-12-11
9 102 4000 2001-12-11
I want the output to be like the one below for the date 2001-12-11
person_101 person_102 person_103
1000 11000 1000
I've tried using the SUM() function but am stuck with the WHERE and JOIN clauses.
How do I solve this?
SELECT
SUM(CASE WHEN person_id = 101 THEN total_amount ELSE 0 END) as person_101,
SUM(CASE WHEN person_id = 102 THEN total_amount ELSE 0 END) as person_102,
SUM(CASE WHEN person_id = 103 THEN total_amount ELSE 0 END) as person_103
FROM
my_table
WHERE
date_time ='2001-12-11'
You need to pivot the data, unfortunately this is not dynamic in mysql, try this:
SELECT
SUM(IF(person_id = 101,total_amount,0)) as person_101,
SUM(IF(person_id = 102,total_amount,0)) as person_102,
SUM(IF(person_id = 103,total_amount,0)) as person_103
FROM
my_table
WHERE
date_time = '2001-12-11'
Also you can do this pivot split by date , you just need to have it in the field list and group by it :
SELECT
date_time,
SUM(IF(person_id = 101,total_amount,0)) as person_101,
SUM(IF(person_id = 102,total_amount,0)) as person_102,
SUM(IF(person_id = 103,total_amount,0)) as person_103
FROM
my_table
GROUP BY
date_time

Help with a nested query in MySQL

I am trying to do some calculations based on record from master table and wanted store manipulated result into a separate test table.
>Table:Master:
>C1 C2 C3 C4
>---------- -------- -- --
>2011-02-19 Test-A 31 3
>2011-02-19 Test-B 34 3
>2011-02-19 Test-C 17 1
>2011-02-15 Test-A* 48 =I 4
>2011-02-15 Test-B 64 6
>2011-02-15 Test-C 55 5
>2011-02-11 Test-A 64 =I2 6
>2011-02-11 Test-B 53 5
>2011-02-11 Test-C 17 1
>2011-02-10 Test-A 12 =I3 1 =J
>2011-02-10 Test-B 02 0
>2011-02-10 Test-C 54 5
Three kinds of test conducted in random fashions in a same day; but for this case date is not much important; only last three test records are used for the calculation.
I am trying to perform sequential calculations as below; using 3rd oldest element. for example, for test A, I(iteration) will be 48 (3rd oldest record = column c3) and therefore R2 & R3 will be calculated based on I2 & I3. And at last displaying average of, R,R2,R3 - J. ( C4 = latest record. )
Expected result:
>Table:Test-A
>SR Date I I2 I3 I4
>-- ---------- ----- ----------- ----------- -------------------
>1 2011/02/17 48 -52.96 -24.18 -10.71
>Formula:
>SR Date R R2 R3 R4
>-- ---------- ----- ----------- ----------- -------------------
>1 today() 48=C3 (I*0.23-I2) (I*0.23-I3) =avg(I,I1,I2,I3)-C4
I guess I need to use sub/nested query with join, but i couldn't able to figure out how to handle I; all result will be placed in individual test tables. Your input will be much appreciated. TIA
Setup test case:
CREATE TABLE `m1`
(c1 DATE
,c2 VARCHAR(6)
,c3 SMALLINT
,c4 TINYINT
) DEFAULT CHARSET=latin1;
INSERT INTO `m1` VALUES
('2011-02-19','Test-A',31,3)
,('2011-02-19','Test-B',34,3)
,('2011-02-19','Test-C',17,1)
,('2011-02-15','Test-A',48,4)
,('2011-02-15','Test-B',64,6)
,('2011-02-15','Test-C',55,5)
,('2011-02-11','Test-A',64,6)
,('2011-02-11','Test-B',53,5)
,('2011-02-11','Test-C',17,1)
,('2011-02-10','Test-A',12,1)
,('2011-02-10','Test-B',02,0)
,('2011-02-10','Test-C',54,5);
This query makes use of one local variable (#i). Provide the test_name ('Test-A') and the date ('2011-02-17') in the query, shown as literals here.
SELECT o.tn AS `Test`
, o.dt AS `Date`
, SUM(CASE WHEN o.n = 1 THEN o.c3*1.00 ELSE NULL END) AS R
, SUM(CASE WHEN o.n = 1 THEN o.c3*0.23 WHEN o.n = 2 THEN -1.00*o.c3 ELSE NULL END) AS R2
, SUM(CASE WHEN o.n = 1 THEN o.c3*0.23 WHEN o.n = 3 THEN -1.00*o.c3 ELSE NULL END) AS R3
, AVG(CASE WHEN o.n < 4 THEN c3*1.00 ELSE NULL END)-SUM(CASE WHEN n = 3 THEN c4*1.00 ELSE NULL END) AS R4
FROM (
SELECT #i := #i + 1 AS n
, s.tn
, s.dt
-- , m.c1
, m.c3
, m.c4
FROM (SELECT '2011-02-17' AS dt,_latin1'Test-A' AS tn, #i := 0) s
JOIN m1 m
ON m.c2 = s.tn AND m.c1 <= s.dt
ORDER BY m.c1 DESC
LIMIT 0,3
) o
GROUP BY o.tn, o.dt
HAVING SUM(1) >= 3
You can run just the inner query, uncomment the m.c1 from the select list, to check the rows returned (1st, 2nd and 3rd latest, prior to the supplied date.
This query returns a different value for R3 than shown in the question, but the result returned by the query appears to be the correct result for the given formula.
Also, the formula for R4 references 5 values: avg(I,I1,I2,I3)-J3. The formula used in the query is effectively =avg(I1,I2,I3)-J3
To get the result for all tests, as of a given date:
SELECT o.tn AS `Test`
, o.dt AS `Date`
, SUM(CASE WHEN o.n = 1 THEN o.c3 ELSE NULL END) AS R
, SUM(CASE WHEN o.n = 1 THEN o.c3*0.23 WHEN o.n = 2 THEN -1.00*o.c3 ELSE NULL END) AS R2
, SUM(CASE WHEN o.n = 1 THEN o.c3*0.23 WHEN o.n = 3 THEN -1.00*o.c3 ELSE NULL END) AS R3
, AVG(CASE WHEN o.n <= 3 THEN c3*1.00 ELSE NULL END)-SUM(CASE WHEN n = 3 THEN c4 ELSE NULL END) AS R4
FROM (
SELECT #i := CASE WHEN #prev_tn = m.c2 THEN #i + 1 ELSE 1 END AS n
, #prev_dt := s.dt AS dt
, #prev_tn := m.c2 AS tn
, m.c1
, m.c3
, m.c4
FROM (SELECT '2011-02-17' AS dt, #i := 0, #prev_tn := NULL) s
JOIN m1 m
ON m.c1 <= s.dt
ORDER BY s.dt, m.c2, m.c1 DESC
) o
GROUP BY o.tn, o.dt
HAVING SUM(1) >= 3
(The HAVING clause guarantees that the query returns results only if there are at least three rows for a given test, preceding the given date.) Here is the query output for two different dates, the 17th and the 20th:
Test Date R R2 R3 R4
------ ---------- -- ------ ------ -----
Test-A 2011-02-17 48 -52.96 -0.96 40.33
Test-B 2011-02-17 64 -38.28 12.72 39.67
Test-C 2011-02-17 55 -4.35 -41.35 37.00
Test Date R R2 R3 R4
------ ---------- -- ------ ------ -----
Test-A 2011-02-20 31 -40.87 -56.87 41.67
Test-B 2011-02-20 34 -56.18 -45.18 45.33
Test-C 2011-02-20 17 -51.09 -13.09 28.67
(The query would be somewhat more involved, to get results for more than one date.)
This may not be the best way to solve the problem, but I've successfully used this approach with MySQL.