This is my table structure:
CUST_ID ORDER_MONTH
---------------------
1 1
1 5
2 3
2 4
My objective is to tag these customers as either New or Returning customers.
When I filter the query lets say for month 1 then customer 1 should have the tag 'New' but when I filter it for month 5 then customer 1 should show up as 'Return' as he already made a purchase in month 1.
Same way customer ID 2 should show up as New for month 3 and return for month 4.
I want to do this using a CASE statement and not inner join.
Thanks
If you insist on using a case statement, the logic would be something like "If this is the first month for that user, write new, otherwise write returning." The query would be as follows:
SELECT CASE
WHEN m.month = (SELECT MIN(month) FROM myTable WHERE customer = m.customer) THEN 'New'
ELSE 'Returning' END AS customerType
FROM myTable m;
However, I think this would be nicer and more readable in a JOIN. You can write an aggregation query to get the earliest month for each user, and then use COALESCE() to replace null values with 'Returning'. The aggregation:
SELECT customer, MIN(month) AS minMonth, 'New' AS customerType
FROM myTable
GROUP BY customer ;
To get the rest:
SELECT m.customer, m.month, COALESCE(t.customerType, 'Returning') AS customerType
FROM myTable m
LEFT JOIN(
SELECT customer, MIN(month) AS minMonth, 'New' AS customerType
FROM myTable
GROUP BY customer) t ON t.customer = m.customer AND t.minMonth = m.month;
Here is an SQL Fiddle example that shows both examples.
You don't need a JOIN and a case statement would probably be overkill...
SELECT CUST_ID, IF(COUNT(1)>1, 'Returning', 'New') AS blah
FROM the_table
WHERE ORDER_MONTH <= the_month
GROUP BY CUST_ID
;
Of course, using just month is going to cause problems after a year (or really, after passing December.)
This would be better
SELECT CUST_ID, IF(COUNT(1)>1, 'Returning', 'New') AS blah
FROM the_table
WHERE order_date <= some_date
GROUP BY CUST_ID
;
Well I do not reccomend this way but this is what you want.
select *
,case when order_month = (select MIN(order_month) from #temp t2 where t1.cust_ID =t2.cust_id) THEN 'NEW' ELSE 'Return' end 'Type'
from #temp t1
I think I get what you're trying to do. Your case statement basically just needs to check if the customer's month equals the month you're filtering by. Something like this:
SELECT
<your other fields>,
CASE WHEN Order_Month = <your filter> THEN 'New'
ELSE 'Return'
END AS 'SomeName'
FROM <your table>
Try this query
select a.CUST_ID, a.ORDER_MONTH ,case when b is not null then 'Return' else 'New' end as type
from tablename a
join tablename b on a.CUST_ID=b.CUST_ID and a.ORDER_MONTH>b.ORDER_MONTH
SELECT *,
CASE
WHEN EXISTS (SELECT *
FROM [YourTable] t2
WHERE t1.cust_id = t2.cust_id
AND t2.order_month < t1.order_month) THEN 'Return'
ELSE 'New'
END
FROM [YourTable] t1
This query uses CASE on an EXISTS clause.
The EXISTS is on a subquery which queries the same table for any rows in previous months.
If there are rows for previous months then the EXISTS is true and the CASE returns 'Return'. If there are no rows for previous months then the EXISTS is false and the CASE returns 'New'.
Related
I have to write a query and check whether sales has increased (use yes) and decreased (use no in column SaleIncreased) for every salesman compared to his previous year's performance.
sample table output should be as follows
EmpID salesman year SaleIncreased
7843921 John 2016 Null
7843921 John 2017 Yes
7843934 Neil 2016 Null
7843934 Neil 2017 No
I have used self join with CASE WHEN statement as follows
select t1.empid, t1.salesman, t1.year
from Sales_temp as t1
inner join Sales_temp as t2
on t1.empid = t2.empid and t2.year = t1.year - 1
case when t1.sale > t2.sale
then 'Yes'
else 'No'
end as 'SaleIncreased'
I'm unable to get the desired output.
Your CASE expression appears to be out of place, and you probably intended for it to be in the SELECT clause:
SELECT
t1.empid,
t1.salesman,
t1.year,
CASE WHEN t1.sale > t2.sale
THEN 'Yes'
ELSE 'No'
END AS SaleIncreased
FROM Sales_temp AS t1
LEFT JOIN Sales_temp AS t2
ON t1.empid = t2.empid AND t2.year = t1.year - 1
ORDER BY
t1.empid,
t1.year;
Another change I made is to use a left join instead of an inner join. This is important, because it would ensure that the earliest year records for each employee would appear in the result set (these would be the records having a NULL value for the increase in sales).
Is this useful.?
DECLARE #tab1 TABLE(EMPID BIGINT,Saleman VARCHAR(100),[Year] BIGINT,Sales BIGINT)
INSERT INTO #tab1
SELECT 7843921,'John',2016,100 Union ALL
SELECT 7843921,'John',2017,150 Union ALL
SELECT 7843934,'Neil',2016,120 Union ALL
SELECT 7843934,'Neil',2017,90
Select *,CASE
WHEN LAG(Sales) OVER(Partition by EmpID order by [year]) IS NULL then NULL
WHEN Sales - LAG(Sales) OVER(Partition by EmpID order by [year])>0 THEN 'Yes'
ELSE 'No' END
from #tab1
I have this two MySQL statements from same table :
SELECT
`table1`.`product_id` As product_id,
COUNT(DISTINCT(table1.user_id)) AS NonebuyersNumber
FROM table1
WHERE status = 1 AND `ispaid` != 2
GROUP BY `table1`.`product_id`
The second statement is :
SELECT l
`table1`.`product_id` As product_id,
COUNT(DISTINCT(table1.user_id)) AS BuyersNumber
FROM table1
WHERE `ispaid` = 1
GROUP BY `table1`.`product_id`
The result that I want is a table like this one :
I tried to use Union but doesn't work because I have two different columns
Any idea how I can get this 3rd table?
Use conditional aggregation:
SELECT
product_id,
COUNT(DISTINCT CASE WHEN status = 1 AND ispaid != 2
THEN user_id ELSE NULL END) AS NonebuyersNumber
COUNT(DISTINCT CASE WHEN ispaid = 1 THEN user_id ELSE NULL END) AS BuyersNumber
FROM table1
WHERE
(status = 1 AND ispaid != 2) OR
ispaid = 1
GROUP BY
product_id;
This should work because both of your queries aggregate over the product_id and the only differences are the WHERE clauses. We can combine the records from both queries and then use CASE expressions to target records intended for each original query.
SELECT t1.product_id AS product_id
SELECT CASE WHEN t1.NonebuyersNumber IS NULL
THEN 0
ELSE t1.NonebuyersNumber
END
AS NonebuyersNumber,
SELECT CASE WHEN t2.BuyersNumber IS NULL
THEN 0
ELSE t2.BuyersNumber
END
AS BuyersNumber
FROM
(SELECT
`table1`.`product_id` As product_id ,
COUNT(DISTINCT(table1.user_id)) AS NonebuyersNumber
FROM table1 WHERE status =1
AND `ispaid` != 2
GROUP BY `table1`.`product_id`)
AS t1
INNER JOIN
(SELECT
`table1`.`product_id` As product_id ,
COUNT(DISTINCT(table1.user_id)) AS BuyersNumber
FROM table1 WHERE `ispaid` = 1
GROUP BY `table1`.`product_id`)
AS t2
ON t1.product_id = t2.product_id
Basically, you need following
Join both the views on product_id
Use CASE statements in select in case one of the buyers numbers is NULL
I'm trying to do multiple selects from one table but it only shown the last select statement.
CREATE PROCEDURE `usp_GetStockCard` (IN Matecode varchar(10))
BEGIN
(select tran_date as tran_date
from TM_matbalance
where Mate_code=Matecode);
(select Mate_code as Mate_code
from TM_matbalance
where Mate_code=Matecode);
(select tran_qtyx as Qty_in
from TM_matbalance
where tran_type='IN'
and mate_code=matecode);
(select tran_qtyx as Qty_out
from TM_matbalance
where tran_type='OUT'
and mate_code=matecode);
END
I've tried to change semicolon to comma after each select statement but it said that syntax error: missing 'semicolon'.
please help.
I look at your problem and I think I solve it.
Basically there is two problems here first one is to pivot your table where your Tran_Qtyx column become Qty_In and Qty_Out based on value in Tran_Type column (IN or OUT)... That part of problem you solve with this query
SELECT Tran_Date, Mate_Code,
SUM(CASE WHEN Tran_Type = 'IN' THEN Tran_Qtyx ELSE 0 END) Qty_In,
SUM(CASE WHEN Tran_Type = 'OUT' THEN Tran_Qtyx ELSE 0 END) Qty_Out
FROM myTable
WHERE Mate_Code = 'MAT001'
GROUP BY DATE(Tran_Date)
NOTE: In your desired result I only see 'MAT001'as Mate_Code so I stick with that in this solution and exclude MAT002 from result.
More about pivot table you can read here, there you can find a link, which is good to take a look, and where you can find a lot of stuff about mysql query's.
The second part of your problem is to get Qty_Balance column. Similar problem is solved here. It's how to calculate row value based on the value in previous row.
So your complete query could look like this:
SELECT t1.Tran_Date, t1.Mate_Code, t1.Qty_In, t1.Qty_Out,
#b := #b + t1.Qty_In - t1.Qty_Out AS Qty_Balance
FROM
(SELECT #b := 0) AS dummy
CROSS JOIN
(SELECT Tran_Date, Mate_Code,
SUM(CASE WHEN Tran_Type = 'IN' THEN Tran_Qtyx ELSE 0 END) Qty_In,
SUM(CASE WHEN Tran_Type = 'OUT' THEN Tran_Qtyx ELSE 0 END) Qty_Out
FROM myTable
WHERE Mate_Code = 'MAT001'
GROUP BY DATE(Tran_Date)) AS t1
ORDER BY t1.Tran_Date;
NOTE: probably only think you should change here is table name and it's should work.
Here is SQL Fiddle so you can see how that's work!
GL!
You will need to structure your query into one, or pass in a parameter to the stored procedure to select which output/query you want, to restructure your query you will need something like:
`CREATE PROCEDURE `usp_GetStockCard` (IN Matecode varchar(10))
BEGIN
(select tran_date as tran_date, Mate_code as Mate_code, tran_qtyx as Qty
from TM_matbalance
where Mate_code=Matecode
and (tran_type='IN' or tran_type='OUT');
END`
Or try this if you have an ID column:
SELECT coalesce(ta.id, tb.id) as tran_id, coalesce(ta.tran_date, tb.tran_date) as tran_date, coalesce(ta.Mate_code, tb.Mate_code) as Mate_code, ta.tran_type as Qty_In, tb.tran_type as Qty_Out
from (select ta.*
from TM_matbalance ta
where ta.tran_type = 'IN'
and Mate_code=Matecode
) ta full outer join
(select tb.*
from TM_matbalance tb
where tb.tran_type = 'OUT'
and Mate_code=Matecode
) tb
on ta.id = tb.id ;
just replace "id" with the name of your ID column if you don't need to return the id column then remove coalesce(ta.id, tb.id) as tran_id
I am trying to write a query that will count and compare the number of rows of two tables on two different databases. If they are equal then a record will be inserted into another table as 'Pass', else it will result in a 'Fail'.
I haven't been able to find any answers through google searches... Here is my query that isn't working:
select
case when
((select count(1) from db1.transaction) = (select count(1) from db2.transaction))
then
insert into db3.validation (test_result) values ('pass')
else
insert into db3.validation (test_result) values ('fail')
end;
You can do this by reversing the order. Do a single insert with a select choosing the value:
insert into db3.validation(test_result)
select (case when t1.cnt1 = t2.cnt2 then 'pass' else 'fail' end)
from (select count(1) as cnt1 from db1.transaction) t1 cross join
(select count(1) as cnt2 from db2.transaction) t2;
Note that I moved the subqueries from the case to a from clause, simply because I find them easier to read this way. You can keep them in the case if you prefer.
How can I get the date for the latest value change in one column with one SQL query?
Possible database situation:
Date State
2012-11-25 state one
2012-11-26 state one
2012-11-27 state two
2012-11-28 state two
2012-11-29 state one
2012-11-30 state one
So result should return 2012-11-29 as latest change state. If I group by State value, I will get the date for first time I have that state in database.
The query will group the table on state and show the state and in the date field the latest date created of that state.
From the given input the output would be
Date State
2012-11-30 state one
2012-11-28 state two
This will get you the last state:
-- Query 1
SELECT state
FROM tableX
ORDER BY date DESC
LIMIT 1 ;
Encapsulating the above, we can use it to get the date just before the last change:
-- Query 2
SELECT t.date
FROM tableX AS t
JOIN
( SELECT state
FROM tableX
ORDER BY date DESC
LIMIT 1
) AS last
ON last.state <> t.state
ORDER BY t.date DESC
LIMIT 1 ;
And then use that to find the date (or the whole row) where the last change occurred:
-- Query 3
SELECT a.date -- can also be used: a.*
FROM tableX AS a
JOIN
( SELECT t.date
FROM tableX AS t
JOIN
( SELECT state
FROM tableX
ORDER BY date DESC
LIMIT 1
) AS last
ON last.state <> t.state
ORDER BY t.date DESC
LIMIT 1
) AS b
ON a.date > b.date
ORDER BY a.date
LIMIT 1 ;
Tested in SQL-Fiddle
And a solution that uses MySQL variables:
-- Query 4
SELECT date
FROM
( SELECT t.date
, #r := (#s <> state) AS result
, #s := state AS prev_state
FROM tableX AS t
CROSS JOIN
( SELECT #r := 0, #s := ''
) AS dummy
ORDER BY t.date ASC
) AS tmp
WHERE result = 1
ORDER BY date DESC
LIMIT 1 ;
I believe this is the answer:
SELECT
DISTINCT State AS State, `Date`
FROM
Table_1 t1
WHERE t1.`Date`=(SELECT MAX(`Date`) FROM Table_1 WHERE State=t1.State)
...and the test:
http://sqlfiddle.com/#!2/8b0d8/5
If you add another column 'changed datetime' you can fill this using an update trigger that inserts NOW(). If you query your table ordering on the changed column, it will endup first.
CREATE TRIGGER `trigger` BEFORE UPDATE ON `table`
FOR EACH ROW
BEGIN
SET ROW.changed = NOW();
END$$
Try this ::
Select
MAX(`Date`), state from mytable
group by state
If you had been using postgres, you could compare different rows in the same table using "LEAD .. OVER" I have not managed to find the same functionallity in mysql.
A bit hairy, but I think this will do:
select min(t1.date) from table_1 t1 where
(select count(distinct state) from table_1 where table_1.date>=t1.date)=1
Basically, this asks for the first time no changes in state is found for any later values. Be warned, it may be this query scales terribly for large data sets....
I think your best choice here are analytical functions. Try this - it should be OK performance-wise:
SELECT *
FROM test
WHERE my_date = (SELECT MAX (my_date)
FROM (SELECT MY_DATE
FROM ( SELECT MY_DATE,
STATE,
LAG (state) OVER (ORDER BY MY_DATE)
lag_val
FROM test
ORDER BY MY_DATE) a
WHERE state != lag_val))
In the inner select, the LAG function gets the previous value in the STATE column and in the outer select I mark the date of a change - those with lag value different than the current state value. And outside, I'm getting the latest date from those dates of a change... I hope that this is what you needed.
SELECT MAX(DATE) FROM YOUR_TABLE
Above answer doesn't seem to satisfy what OP needs.
UPDATED ANSWER WITH AFTER INSERT/UPDATE TRIGGER
DELCARE #latestState varchar;
DELCARE #latestDate date;
CREATE TRIGGER latestInsertTrigger AFTER INSERT ON myTable
FOR EACH ROW
BEGIN
IF OLD.DATE <> NEW.date THEN
SET #latestState = NEW.state
SET #latestDate = NEW.date
END IF
END
;
CREATE TRIGGER latestUpdateTrigger AFTER UPDATE ON myTable
FOR EACH ROW
BEGIN
IF OLD.DATE = NEW.date AND OLD.STATE <> NEW.STATE THEN
SET #latestState = NEW.state
SET #latestDate = NEW.date
END IF
END
;
You may use the following query to get the latest record added/updated:
SELECT DATE, STATE FROM myTable
WHERE STATE = #latestState
OR DATE = #latestDate
ORDER BY DATE DESC
;
Results:
DATE STATE
November, 30 2012 00:00:00+0000 state one
November, 28 2012 00:00:00+0000 state two
November, 27 2012 00:00:00+0000 state two
The above query results needs to be limitted to 2, 3 or n based on what you need.
Frankly it seems like you want to get max from both columns based on the data sample you have given. Assuming that your state only increases with the date. Only I wish if the state was an integer :D
Then union of two max sub queries on both columns would have solved it easily. Still a string manipulation regex can find what's the max in state column. Finally this approach needs limit x. However it still has lope hole. Anyway it took me sometime to figure your need out :$