Using IF or Case with multiple in SQL Statement - sql

I want to do something like this
this works
Select ID, number, cost from table order by number
number can be 2-xtimes but the cost and the same
1 A33 66.50
2 A34 73.50
3 A34 73.50
But I want to have
1 A33 66.50
2 A34 73.50
3 A34 0
I want to change it in the Sql to 0
I tried distinct or if then else.
I want to do something like this
declare #oldcost int;
Select ID, number,
if(cost=#oldcost) then
cost=0;
else
cost=cost;
end if
#oldcost=cost;
from table order by number
How can I do it in SQL?

You can use window functions and a case expression:
select ID, number,
(case when row_number() over (partition by number order by id) = 1
then cost else 0
end) as cost
from table
order by number, id;
Note that SQL generally does not take ordering into account, so results can be returned in any order -- and even with an order by, rows with the same keys can be in any order (and in different orders on different executions).
Hence, the order by includes id as well as number so you get the cost on the "first" row for each number.

Related

if count value of the column is greater than 1, I want to print the count of the column else I want to print value in the field

I am writing a query which fetches details from different tables. In one column I want to print count value of a column. If the count value of the column is greater than 1, I want to print the count of the column else I want to print value in the field.
I want to build a query which will give me count of user_id from table 1 & 2. if the count user_id is greater than 1, then print count (user_id) else print value of user_id
Table:1
| user_id |
| John |
| Bob |
| Kris |
| Tom |
Table:2
| user_id |
| Rob |
query result should list count of table1 as it greater than 1. Table2 should list Rob as it is lesser than 2
You want to select user IDs (names actually) from a table. If it's just one row then show that name, otherwise show the number of entries instead. So, just use a CASE expression to check whether count is 1 or greater than 1.
You probably need CAST or CONVERT to turn the count number into a string, so the CASE expression always returns the same type (this is how CASE works).
select
case when count(*) > 1
then cast(count(*) as varchar(100))
else max(user_id)
end as name_or_count
from mytable
Window Functions come to mind but since your user_ids are not numbers, you'll run into an issue where you can't have two different data types in the same column. See how this works for you. Make sure to cast the varchar numbers back to integer if this script is part of a larger process.
with cte as
(select 'John' as user_id union all
select 'Bob' as user_id union all
select 'Kris' as user_id union all
select 'Tom' as user_id)
select distinct case when count(*) over() > 1
then cast(count(*) over() as varchar) else user_id end
from cte
with cte as
(select 'Rob' as user_id)
select distinct case when count(*) over() > 1
then cast(count(*) over() as varchar) else user_id end
from cte

Case Statement for multiple criteria

I would like to ignore some of the results of my query as for all intents and purposes, some of the results are a duplicate, but based on the way the request was made, we need to use this hierarchy and although we are seeing different 'Company_Name' 's, we need to ignore one of the results.
Query:
SELECT
COUNT(DISTINCT A12.Company_name) AS Customer_Name_Count,
Company_Name,
SUM(Total_Sales) AS Total_Sales
FROM
some_table AS A12
GROUP BY
2
ORDER BY
3 ASC, 2 ASC
This code omits half a doze joins and where statements that are not germane to this question.
Results:
Customer_Name_Count Company_Name Total_Sales
-------------------------------------------------------------
1 3 Blockbuster 1,000
2 6 Jimmy's Bar 1,500
3 6 Jimmy's Restaurant 1,500
4 9 Impala Hotel 2,000
5 12 Sports Drink 2,500
In the above set, we can see that numbers 2 & 3 have the same count and the same total_sales number and similar company names. Is there a way to create a case statement that takes these 3 factors into consideration and then drops one or the other for Jimmy's enterprises? The other issue is that this has to be variable as there are other instances where this happens. And I would only want this to happen if the count and sales number match each other with a similar name in the company name.
Desired result:
Customer_Name_Count Company_Name Total_Sales
--------------------------------------------------------------
1 3 Blockbuster 1,000
2 6 Jimmy's Bar 1,500
3 9 Impala Hotel 2,000
4 12 Sports Drink 2,500
Looks like other answers are accurate based on assumption that Company_IDs are the same for both.
If Company_IDs are different for both Jimmy's Bar and Jimmy's Restaurant then you can use something like this. I suggest you get functional users involved and do some data clean-up else you'll be maintaining this every time this issue arise:
SELECT
COUNT(DISTINCT CASE
WHEN A12.Company_Name = 'Name2' THEN 'Name1'
ELSE A12.Company_Name
END) AS Customer_Name_Count
,CASE
WHEN A12.Company_Name = 'Name2' THEN 'Name1'
ELSE A12.Company_Name
END AS Company_Name
,SUM(A12.Total_Sales) AS Total_Sales
FROM some_table er
GROUP BY CASE
WHEN A12.Company_Name = 'Name2' THEN 'Name1'
ELSE A12.Company_Name
END
Your problem is that the joins you are using are multiplying the number of rows. Somewhere along the way, multiple names are associated with exactly the same entity (which is why the numbers are the same). You can fix this by aggregating by the right id:
SELECT COUNT(DISTINCT A12.Company_name) AS Customer_Name_Count,
MAX(Company_Name) as Company_Name,
SUM(Total_Sales) AS Total_Sales
FROM some_table AS A12
GROUP BY Company_id -- I'm guessing the column is something like this
ORDER BY 3 ASC, 2 ASC;
This might actually overstate the sales (I don't know). Better would be fixing the join so it only returned one name. One possibility is that it is a type-2 dimension, meaning that there is a time component for values that change over time. You may need to restrict the join to a single time period.
You need to have function to return a common name for the companies and then use DISTINCT:
SELECT DISTINCT
Customer_Name_Count,
dbo.GetCommonName(Company_Name) as Company_Name,
Total_Sales
FROM dbo.theTable
You can try to use ROW_NUMBER with window function to make row number by Customer_Name_Count and Total_Sales then get rn = 1
SELECT * FROM (
SELECT *,ROW_NUMBER() OVER(PARTITION BY Customer_Name_Count,Total_Sales ORDER BY Company_Name) rn
FROM (
SELECT
COUNT(DISTINCT A12.Company_name) AS Customer_Name_Count,
Company_Name,
SUM(Total_Sales) AS Total_Sales
FROM
some_table AS A12
GROUP BY
Company_Name
)t1
)t1
WHERE rn = 1

Oracle- logically give each set of rows a group based on a value from ordered list

I have this
SQL Fiddle
When ordered by the sequence_number field, these records need to be grouped and given a row_number based on the following logic:
All records for which the following line type is not a 0 is part of the same group.
Example, from the provided SQL fiddle,
sequence numbers 0,1 and 2 are part of the same group, and sequence numbers 3 and 4 are part of another group. Basically, any rows up to a 0 line type are part of a single group. The data I am trying to return will look like:
GROUP LINE_TYPE SEQUENCE_NUMBER PRODUCT
------------------------------------------------
1 0 0 REM322
1 6 1 Discount
1 7 2 Loyalty Discount
2 0 3 RGM32
2 6 4 Discount
Another way to re-word what I am after is that when ordered by the sequence number, the group number will change when it hit's a 0.
I've been racking my brain trying to think how to do this using partitions/lags and even self joins but am having trouble.
Any help appreciated.
Set the column value to 1 if line_type is 0 and then calculate the running sum(using SUM as analytical function) over this.
select sum(case when line_type = 0 then 1
else 0 end
) over (order by sequence_number) as grp,
line_type,
sequence_number,
product
from ret_trand
order by sequence_number;
Demo.
Another way of doing the grouping is using a hierarchical query and CONNECT_BY_ROOT:
SELECT CONNECT_BY_ROOT sequence_number AS first_in_sequence,
line_type,
sequence_number,
product
FROM ret_trand
START WITH
line_type = 0
CONNECT BY
( sequence_number - 1 = PRIOR sequence_number
AND line_type <> 0)
ORDER SIBLINGS BY
sequence_number;
SQLFIDDLE
This will identify the groups by the initial sequence number of the group.
If you want to change this to a sequential ranking for the groups then you can use DENSE_RANK to do this:
WITH first_in_sequences AS
(
SELECT CONNECT_BY_ROOT sequence_number AS first_in_sequence,
line_type,
sequence_number,
product
FROM ret_trand
START WITH
line_type = 0
CONNECT BY
( sequence_number - 1 = PRIOR sequence_number
AND line_type <> 0)
ORDER SIBLINGS BY
sequence_number
)
SELECT DENSE_RANK() OVER ( ORDER BY first_in_sequence ) AS "group",
line_type,
sequence_number,
product
FROM first_in_sequences;
SQLFIDDLE

Update rows in table

I have a table (Fruits) with following column
Fruit_Name(varchar2(10)) | IsDuplicate Number(1)
Mango 0
Orange 0
Mango 0
What i have to do is to update IsDuplicate column to 1 where Fruit_Name in Distinct i.e
Fruit_Name(varchar2(10)) | IsDuplicate Number(1)
Mango 1
Orange 1
Mango 0
How should I do this?
This should do it as far as I can tell
update fruits
set is_duplicate =
(
select case
when dupe_count > 1 and row_num = 1 then 1
else 0
end as is_dupe
from (
select f2.fruit_name,
count(*) over (partition by f2.fruit_name) as dupe_count,
row_number() over (partition by f2.fruit_name order by f2.fruit_name) as row_num,
rowid as row_id
from fruits f2
) ft
where ft.row_id = fruits.rowid
and ft.fruit_name = fruits.fruit_name
)
Edit
But instead of actually updating the table, why don't you create a view that returns the information. Depending on the size of the table it might be more efficient.
create view fruit_dupe_view
as
select fruit_name,
case
when dupe_count > 1 and row_num = 1 then 1
else 0
end as is_duplicate
from (
select fruit_name,
count(*) over (partition by fruit_name) as dupe_count,
row_number() over (partition by fruit_name order by fruit_name) as row_num
from fruits
) ft
Straight and simple -- you can't. Not with vanilla SQL. SQL is a set-based processing language, and you do things in sets. There is no way for SQL to know which one of your many Mango's should be tagged 1. You can probably tag one of them with 1 using windowing functions or ROWNUM etc. in a SELECT, but I don't think it can be done with an UPDATE.
In other words, your table lacks a unique key in the first place, so it is not something that SQL is designed to process.
However, you may try adding a sequential primary key to each row. Then you can easily write an UPDATE query to set to 1 all the rows with COUNT > 1 and key = MIN(key).
In other words, you really have to look at your database design. Relational databases are not supposed to contain "duplicates". That fact that you need to mark something as a duplicate means that your tables are designed wrong in the first place. The database should not even allow duplications to enter into its data.

Grouping by intervals

Given a table (mytable) containing a numeric field (mynum), how would one go about writing an SQL query which summarizes the table's data based on ranges of values in that field rather than each distinct value?
For the sake of a more concrete example, let's make it intervals of 3 and just "summarize" with a count(*), such that the results tell the number of rows where mynum is 0-2.99, the number of rows where it's 3-5.99, where it's 6-8.99, etc.
The idea is to compute some function of the field that has constant value within each group you want:
select count(*), round(mynum/3.0) foo from mytable group by foo;
I do not know if this is applicable to mySql, anyway in SQL Server I think you can "simply" use group by in both the select list AND the group by list.
Something like:
select
CASE
WHEN id <= 20 THEN 'lessthan20'
WHEN id > 20 and id <= 30 THEN '20and30' ELSE 'morethan30' END,
count(*)
from Profiles
where 1=1
group by
CASE
WHEN id <= 20 THEN 'lessthan20'
WHEN id > 20 and id <= 30 THEN '20and30' ELSE 'morethan30' END
returns something like
column1 column2
---------- ----------
20and30 3
lessthan20 3
morethan30 13