How to set value based on value existence in SQL Server? - sql

I have the following T-SQL code:
select
id,
(case
when n in(Bla1', 'Bla2') then 1
when n = 'Bla3' then 99
else 0
end) as c
from
hello
Running this code outputs this result:
| id | c |
+--------+----+
| 577140 | 0 |
| 577140 | 1 |
| 577140 | 0 |
| 577140 | 0 |
| 577140 | 99 |
| 577141 | 0 |
| 577141 | 0 |
| 577141 | 0 |
| 577142 | 0 |
| 577142 | 0 |
| 577142 | 1 |
How can I modify the code to get the following output?
| id | c |
+--------+----+
| 577140 | 99 |
| 577141 | 0 |
| 577142 | 1 |
Rule
For each id: If 99 exists, then c becomes 99. If not, either 1 or 0, depending if any 1 exists.

You can use aggregation:
select id,
max(case when n in ('Bla1', 'Bla2') then 1
when n = 'Bla3' then 99
else 0
end) as c
from hello
group by id;

Related

How to assign duplicate increment in SQL?

While going through SQL columns, if we find text match "NEW" in Calc column, update the incrementing a count starting with 1 in Results column.
It should look like this on the output:
The following uses an id column to resolve the order issue. Replace that with your corresponding expression. This also addresses the requirement to start the display sequence with 1 and also show 0 for the 'NEW' rows.
The SQL (updated):
SELECT logs.*
, CASE WHEN text = 'NEW' THEN 0
ELSE
COALESCE(SUM(CASE WHEN text = 'NEW' THEN 1 END) OVER (PARTITION BY xrank ORDER BY id)+1, 1)
END AS display
FROM logs
ORDER BY id
The result:
+----+-------+------+---------+
| id | xrank | text | display |
+----+-------+------+---------+
| 1 | 1 | A | 1 |
| 2 | 1 | B | 1 |
| 3 | 1 | C | 1 |
| 4 | 1 | NEW | 0 |
| 5 | 1 | D | 2 |
| 6 | 1 | Q | 2 |
| 7 | 1 | B | 2 |
| 8 | 1 | NEW | 0 |
| 9 | 1 | D | 3 |
| 10 | 1 | Z | 3 |
| 11 | 2 | A | 1 |
| 12 | 2 | B | 1 |
| 13 | 2 | C | 1 |
| 14 | 2 | NEW | 0 |
| 15 | 2 | D | 2 |
| 16 | 2 | Q | 2 |
| 17 | 2 | B | 2 |
| 18 | 2 | NEW | 0 |
| 19 | 2 | D | 3 |
| 20 | 2 | Z | 3 |
+----+-------+------+---------+
You need a column that specifies the ordering for the table. With that, just use a cumulative sum:
select t.*,
1 + sum(case when Calc = 'NEW' then 1 else 0 end) over (partition by Rank_Id order by Seq) as display
from t;

T-SQL - subqueries on rows grouped by ID to create a summary table

I have a table "MyTable" with an id and two int fields "A" and "B":
MyTable
+-------+-----+-----+
| ID | A | B |
+-------+-----+-----+
| 99 | 0 | 1 |
| 99 | 0 | 1 |
| 99 | 0 | 0 |
| 99 | 1 | 1 |
| 99 | 0 | 1 |
| 100 | 1 | 0 |
| 100 | 0 | 0 |
| 100 | 0 | 0 |
| 444 | 1 | 0 |
| 88 | 0 | 0 |
| 88 | 0 | 0 |
| 88 | 0 | 1 |
+-------+-----+-----+
I'd like a T-SQL query to return a single row for each distinct id, which contains:
each distinct ID
whether there exists a row for that ID with a non-zero value for "a"
whether there exists a row for that ID with a non-zero value for "b"
like so:
+-------+-----+-----+
| ID | A | B |
+-------+-----+-----+
| 99 | 1 | 1 |
| 100 | 1 | 0 |
| 444 | 1 | 0 |
| 88 | 0 | 1 |
+-------+-----+-----+
I can GROUP BY the ID, but I don't know how to create the joins or subqueries on each group to get the desired result.
select id, max(case when A<>0 then 1 else 0 end)A, max(case when B<>0 then 1 else 0 end)B
from mytable
group by id
Or you can just use since your value is 1 and 0. But if value is other than that please use first query.
select id, max(A)A, max(B)B
from mytable
group by id

SQL - Partition restarted based on a column value

I need to create a new column that restarts at every 0 value of Column Repeated Call of each Customer_ID:
+-------------+---------+----------------------+---------------+
| Customer_ID | Call_ID | Days Since Last Call | Repeated Call |
+-------------+---------+----------------------+---------------+
| 1 | 1 | Null | 0 |
| 1 | 2 | 45 | 0 |
| 1 | 3 | 0 | 1 |
| 1 | 4 | 0 | 1 |
| 1 | 5 | 0 | 1 |
| 1 | 6 | 48 | 0 |
| 1 | 7 | 1 | 1 |
| 2 | 8 | Null | 0 |
| 2 | 9 | 1 | 1 |
+-------------+---------+----------------------+---------------+
In to something like this:
+-------------+---------+----------------------+---------------+-------------+
| Customer_ID | Call_ID | Days Since Last Call | Repeated Call | Order_Group |
+-------------+---------+----------------------+---------------+-------------+
| 1 | 1 | Null | 0 | 1 |
| 1 | 2 | 45 | 0 | 2 |
| 1 | 3 | 0 | 1 | 2 |
| 1 | 4 | 0 | 1 | 2 |
| 1 | 5 | 0 | 1 | 2 |
| 1 | 6 | 48 | 0 | 3 |
| 1 | 7 | 1 | 1 | 3 |
| 2 | 8 | Null | 0 | 1 |
| 2 | 9 | 1 | 1 | 1 |
+-------------+---------+----------------------+---------------+-------------+
Appreciate your suggestion, thanks!
You can use SUM() window function:
select t.*,
sum(case when Repeated_Call = 0 then 1 else 0 end)
over (partition by Customer_ID order by Call_Id) Order_Group
from tablename t
See the demo (for MySql but it is standard SQL).
Results:
| Customer_ID | Call_ID | Days Since Last Call | Repeated_Call | Order_Group |
| ----------- | ------- | -------------------- | ------------- | ----------- |
| 1 | 1 | | 0 | 1 |
| 1 | 2 | 45 | 0 | 2 |
| 1 | 3 | 0 | 1 | 2 |
| 1 | 4 | 0 | 1 | 2 |
| 1 | 5 | 0 | 1 | 2 |
| 1 | 6 | 48 | 0 | 3 |
| 1 | 7 | 1 | 1 | 3 |
| 2 | 8 | | 0 | 1 |
| 2 | 9 | 1 | 1 | 1 |
You can calculation every 0 value in column Repeated Call (for each customer) using window analytic function COUNT with ROWS UNBOUNDED PRECEDING:
SELECT *,
COUNT(CASE WHEN Repeated Call=0 THEN 1 ELSE NULL END )OVER(PARTITION BY Customer_ID
ORDER BY Call_ID ROWS UNBOUNDED PRECEDING)Order_Gr FROM Table

copy template data sets into same table

I'm searching for a sql query that copy entries based on a template into the same table.
the simplified table looks as follows:
product | model | version | attribute | attributeValue
----------|--------|----------|---------------|----------------
A | 1 | 0 | price | 1000
A | 1 | 0 | token | 1
A | 1 | 0 | spc_weight | 130
A | 1 | 0 | spc_volume | 150
A | 1 | 0 | spc_diameter | 12
A | 1 | 0 | colour | blue
A | 1 | 1 | price | 1100
A | 1 | 1 | token | 1
A | 1 | 1 | spc_weight | 130
A | 1 | 1 | spc_volume | 150
A | 1 | 1 | spc_diameter | 12
A | 1 | 1 | colour | blue
B | 1 | 0 | price | 800
B | 1 | 0 | token | 1
B | 1 | 0 | spc_weight | 135
B | 1 | 0 | spc_volume | 150
B | 1 | 0 | spc_diameter | 12
B | 1 | 0 | colour | red
B | 2 | 0 | price | 800
B | 2 | 0 | token | 1
B | 2 | 0 | spc_weight | 140
B | 2 | 0 | spc_volume | 150
B | 2 | 0 | spc_diameter | 12
B | 2 | 0 | colour | red
C | 1 | 0 | price | 800
C | 1 | 0 | token | 2
C | 1 | 0 | spc_weight | 135
C | 1 | 0 | spc_volume | 155
C | 1 | 0 | spc_diameter | 12
C | 1 | 0 | colour | green
i have also created here the table --> http://sqlfiddle.com/#!18/c6b01/3
what's the template? the template product (product+model+version) is user defined.
what data should be copied? all spc_% attributeValue from template product
where should the data be copied? the attributeValue from the template needs to copied into the corresponding attribute from all product that have the same token attribute and are the latest version.
what I have at the moment is
get the template data
select attributeValue from product
where product='B' and model =1 and version =0 and attribute like 'spc_%'
what products are affected
select product, model, max (version) as version from product
where attribute='token' and attributeValue='1'
group by product, model
so the solution in that case would be that all the attribute spc_% for product A model 1 version 1 and product B model 2 version 0 are populated with the attributeValue from the template product b model 1 version 0
thats how the table should look after the update. the values with * are the one that should have been updated based on the template.
product | model | version | attribute | attributeValue
----------|--------|----------|---------------|----------------
A | 1 | 0 | price | 1000
A | 1 | 0 | token | 1
A | 1 | 0 | spc_weight | 130
A | 1 | 0 | spc_volume | 150
A | 1 | 0 | spc_diameter | 12
A | 1 | 0 | colour | blue
A | 1 | 1 | price | 1100
A | 1 | 1 | token | 1
A | 1 | 1 | spc_weight | 135*
A | 1 | 1 | spc_volume | 150*
A | 1 | 1 | spc_diameter | 12*
A | 1 | 1 | colour | blue
B | 1 | 0 | price | 800
B | 1 | 0 | token | 1
B | 1 | 0 | spc_weight | 135
B | 1 | 0 | spc_volume | 150
B | 1 | 0 | spc_diameter | 12
B | 1 | 0 | colour | red
B | 2 | 0 | price | 800
B | 2 | 0 | token | 1
B | 2 | 0 | spc_weight | 135*
B | 2 | 0 | spc_volume | 150*
B | 2 | 0 | spc_diameter | 12*
B | 2 | 0 | colour | red
C | 1 | 0 | price | 800
C | 1 | 0 | token | 2
C | 1 | 0 | spc_weight | 135
C | 1 | 0 | spc_volume | 155
C | 1 | 0 | spc_diameter | 12
C | 1 | 0 | colour | green
thats where I stuck right now. somehow I'm not able to write the inner join and then the update function.
anyone some advice?
it seems that this sql does the job:
with templateCTE as ( select attribute, attributeValue
from product
where product='A'
and attribute like 'spc_%'
and model=1
and version=0),
productsToUpdate as ( select product, model, max (version) as version from product
where attribute='token' and attributeValue='1'
group by product, model ),
selectionToJoin as (select productsToUpdate.product, productsToUpdate.model,productsToUpdate.version, product.attribute, product.attributeValue from productsToUpdate
inner join product on productsToUpdate.product = product.product and productsToUpdate.model= product.model and productsToUpdate.version= product.version)
UPDATE P1
SET P1.[attributeValue] = P2.[attributeValue]
FROM selectionToJoin P1
JOIN templateCTE P2 ON P1.[attribute] = P2.[attribute]
COMMIT;
SELECT * FROM product
regards
You can first select the source rows using a sub query then use join the product table using that resultset. Below update query will work for you -
WITH CTE AS (SELECT [attribute], [attributeValue]
FROM product
WHERE [attribute] LIKE 'spc_%'
AND [product] = 'B'
AND [model] = 1
AND [version] = 0)
UPDATE P1
SET P1.[attributeValue] = P2.[attributeValue]
FROM product P1
JOIN CTE P2 ON P1.[attribute] = P2.[attribute]
Here is the fiddle.

SQL how to force to display row with 0 if no data available?

My table returns results as following (skips row if HourOfDay does not have data for particular ID)
ID HourOfDay Counts
--------------------------
1 5 5
1 13 10
1 23 3
..........................HourOfDay up till 23
2 9 1
and so on.
What I am trying to achieve is to force showing rows displaying 0 for HoursOfDay, which don't have data, like following:
ID HourOfDay Counts
--------------------------
1 0 0
1 1 0
1 2 0
1......................
1 5 5
1 6 0
1......................
1 23 3
2 0 0
2 1 0
etc.
I have researched around about it. It looks like I can achieve this result if I create an extra table and outer join it. So I have created table variable in SP (as a temp workaround)
DECLARE #Hours TABLE
(
[Hour] INT NULL
);
INSERT INTO #Hours VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
,(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23);
However, no matter how I join it, it does not achieve desired result.
How do I proceed? Do I add extra columns to join on? Completely different approach? Any hint in the right direction is appreciated!
Using a derived table for the distinct Ids cross joined to #Hours, left joined to your table:
select
i.Id
, h.Hour
, coalesce(t.Counts,0) as Counts
from (select distinct Id from t) as i
cross join #Hours as h
left join t
on i.Id = t.Id
and h.Hour = t.HourOfDay
rextester demo: http://rextester.com/XFZYX88502
returns:
+----+------+--------+
| Id | Hour | Counts |
+----+------+--------+
| 1 | 0 | 0 |
| 1 | 1 | 0 |
| 1 | 2 | 0 |
| 1 | 3 | 0 |
| 1 | 4 | 0 |
| 1 | 5 | 5 |
| 1 | 6 | 0 |
| 1 | 7 | 0 |
| 1 | 8 | 0 |
| 1 | 9 | 0 |
| 1 | 10 | 0 |
| 1 | 11 | 0 |
| 1 | 12 | 0 |
| 1 | 13 | 10 |
| 1 | 14 | 0 |
| 1 | 15 | 0 |
| 1 | 16 | 0 |
| 1 | 17 | 0 |
| 1 | 18 | 0 |
| 1 | 19 | 0 |
| 1 | 20 | 0 |
| 1 | 21 | 0 |
| 1 | 22 | 0 |
| 1 | 23 | 3 |
| 2 | 0 | 0 |
| 2 | 1 | 0 |
| 2 | 2 | 0 |
| 2 | 3 | 0 |
| 2 | 4 | 0 |
| 2 | 5 | 0 |
| 2 | 6 | 0 |
| 2 | 7 | 0 |
| 2 | 8 | 0 |
| 2 | 9 | 1 |
| 2 | 10 | 0 |
| 2 | 11 | 0 |
| 2 | 12 | 0 |
| 2 | 13 | 0 |
| 2 | 14 | 0 |
| 2 | 15 | 0 |
| 2 | 16 | 0 |
| 2 | 17 | 0 |
| 2 | 18 | 0 |
| 2 | 19 | 0 |
| 2 | 20 | 0 |
| 2 | 21 | 0 |
| 2 | 22 | 0 |
| 2 | 23 | 0 |
+----+------+--------+