How to get middle character from a string using SQL Server? - sql

I have a table like this.
----------------
|Id | Name |
----------------
|1 |Apple |
|2 |banana |
|3 |Orange |
|4 |Grapes |
|5 |Mango |
----------------
I need middle character from each string.My result is to be like this
--------------------
|Id | Name |MidChar|
------------------------
|1 |Apple | p |
|2 |banana | n(or)a|
|3 |Orange | a(or)n|
|4 |Grape | a |
|5 |Mango | n |
-------------------
Please give some solutions or ideas.

Use LEN and Substring function
Try this
declare #Name varchar(10) = 'Apple'
select substring(#Name,LEN(#Name)/2+1,1)

I would suggest you try the below query. It should suit all your specified requirements.
SELECT [Id],[Name],CASE WHEN LEN(Name)%2 != 0
THEN SUBSTRING(Name,LEN(Name)/2+1,1)
ELSE SUBSTRING(Name,LEN(Name)/2,1)+' (or) '+ SUBSTRING(Name,LEN(Name)/2 + 1,1)
END AS [MidChar]
FROM [yourTable]
Kindly share your feedback.

Try the below script,
SELECT Id,Name,SUBSTRING(Name,CEILING(LEN(Name)/2.0),1) AS MidChar
FROM MyTable

Related

In PostgreSQL, conditionally count rows

Background
I'm a novice Postgres user running a local server on a Windows 10 machine. I've got a dataset g that looks like this:
+--+---------+----------------+
|id|treatment|outcome_category|
+--+---------+----------------+
|a |1 |cardiovascular |
|a |0 |cardiovascular |
|b |0 |metabolic |
|b |0 |sensory |
|c |1 |NULL |
|c |0 |cardiovascular |
|c |1 |sensory |
|d |1 |NULL |
|d |0 |cns |
+--+---------+----------------+
The Problem
I'd like to get a count of outcome_category by outcome_category for those id who are "ever treated" -- defined as "id's who have any row where treatment=1".
Here's the desired result:
+----------------+---------+
|outcome_category| count |
+----------------+---------+
|cardiovascular | 3 |
|sensory | 1 |
|cns | 1 |
+----------------+---------+
It would be fine if the result had to contain metabolic, like so:
+----------------+---------+
|outcome_category|treatment|
+----------------+---------+
|cardiovascular | 3 |
|metabolic | 0 |
|sensory | 1 |
|cns | 1 |
+----------------+---------+
Obviously I don't need the rows to be in any particular order, though descending would be nice.
What I've tried
Here's a query I've written:
select treatment, outcome_category, sum(outcome_ct)
from (select max(treatment) as treatment,
outcome_category,
count(outcome_category) as outcome_ct
from g
group by outcome_category) as sub
group by outcome_category, sub.treatment;
But it's a mishmash result:
+---------+----------------+---+
|treatment|outcome_category|sum|
+---------+----------------+---+
|1 |cardiovascular |3 |
|1 |sensory |2 |
|0 |metabolic |1 |
|1 |NULL |0 |
|0 |cns |1 |
+---------+----------------+---+
I'm trying to identify the "ever exposed" id's using that first line in the subquery: select max(treatment) as treatment. But I'm not quite getting at the rest of it.
EDIT
I realized that the toy dataset g I originally gave you above doesn't correspond to the idiosyncrasies of my real dataset. I've updated g to reflect that many id's who are "ever treated" won't have a non-null outcome_category next to a row with treatment=1.
Interesting little problem. You can do:
select
outcome_category,
count(x.id) as count
from g
left join (
select distinct id from g where treatment = 1
) x on x.id = g.id
where outcome_category is not null
group by outcome_category
order by count desc
Result:
outcome_category count
----------------- -----
cardiovascular 3
sensory 1
cns 1
metabolic 0
See running example at db<>fiddle.
This would appear to be just a simple aggregation,
select outcome_category, Count(*) count
from t
where treatment=1
group by outcome_category
order by Count(*) desc
Demo fiddle

Group by a period Postgresql

I'm trying to group by a period of time the following table (example) :
------------------
|month|year|value|
------------------
|7 |2019|1.2 |
|8 |2019|1.7 |
|9 |2019|1.5 |
|10 |2019|0.7 |
|11 |2019|0.2 |
|12 |2019|1.7 |
|1 |2020|1.0 |
|2 |2020|0.1 |
|3 |2020|2.1 |
|4 |2020|1.2 |
|5 |2020|1.2 |
|6 |2020|1.7 |
|7 |2020|2.1 |
|8 |2020|1.7 |
|9 |2020|1.5 |
|10 |2020|0.7 |
|11 |2020|0.2 |
|12 |2020|1.7 |
|1 |2021|1.0 |
|2 |2021|0.1 |
|3 |2021|2.1 |
|4 |2021|1.2 |
|5 |2021|1.7 |
|6 |2021|1.5 |
Etc..
I have to group every 12 month from july(7) to June(6 of the next year).
I already tried some solution found online but nothing work for me, anyone have a solution?
I'm using Postgresql.
Thanks in advance
One way is to use arithmetic
select floor((year * 12 + month - 7) / 12) as effective_year, avg(value)
from t
group by effective_year;
#GordonLinoff has the appropriate solution missing only the actual period covered by the effective_year. However, that period is easily derived using the effective_year and a couple built in functions: daterange and make_date.
select daterange(make_date(effective_year ,07 ,01)
,make_date(effective_year+1,06 ,30)
,'[]'
)
, avg_value
from (select floor((year * 12 + month - 7) / 12)::integer as effective_year
, avg(value) avg_value
from test_data
group by effective_year
) da
order by effective_year;
See full example.

Update a column value within a SELECT query

I have a complicated SQL question.
Can we update a column within a SELECT query? Example:
Consider this table:
|ID |SeenAt |
----------------
|1 |20 |
|1 |21 |
|1 |22 |
|2 |70 |
|2 |80 |
I want a SELECT Query that gives for each ID when was it seen for the first time. And when did it seen 'again':
|ID |Start |End |
---------------------
|1 |20 |21 |
|1 |20 |22 |
|1 |20 |22 |
|2 |70 |80 |
|2 |70 |80 |
First, both columns Start and End would have the same value, but when a second row with the same ID is seen we need to update its predecessor to give End the new SeenAt value.
I succeeded to create the Start column, I give the minimum SeenAt value per ID to all IDs. But I can't find a way to update the End column everytime.
Don't mind the doubles, I have other columns that change in every new row
Also, I am working in Impala but I can use Oracle.
I hope that I have been clear enough. Thank you
You could use lead() and nvl():
select id, min(seenat) over (partition by id) seen_start,
nvl(lead(seenat) over (partition by id order by seenat), seenat) seen_end
from t
demo
Start is easy just the MIN of the GROUP
End you need to find the MIN after the SeenAt and in case you don't find it then the current SeenAt
SQL DEMO
SELECT "ID",
(SELECT MIN("SeenAt")
FROM Table1 t2
WHERE t1."ID" = t2."ID") as "Start",
COALESCE(
(SELECT MIN("SeenAt")
FROM Table1 t2
WHERE t1."ID" = t2."ID"
AND t1."SeenAt" < t2."SeenAt")
, t1."SeenAt"
) as End
FROM Table1 t1
OUTPUT
| ID | START | END |
|----|-------|-----|
| 1 | 20 | 21 |
| 1 | 20 | 22 |
| 1 | 20 | 22 |
| 2 | 70 | 80 |
| 2 | 70 | 80 |
you seem to need min() analytic function with a self-join:
select distinct t1.ID,
min(t1.SeenAt) over (partition by t1.ID order by t1.ID) as "Start",
t2.SeenAt as "End"
from tab t1
join tab t2 on t1.ID=t2.ID and t1.SeenAt<=t2.SeenAt
order by t2.SeenAt;
Demo

Better way of writing my SQL query with conditional group by

Here's my data
|vendorname |total|
---------------------
|Najla |10 |
|Disney |20 |
|Disney |10 |
|ToysRus |5 |
|ToysRus |1 |
|Gap |1 |
|Gap |2 |
|Gap |3 |
|Najla |2 |
Here's the resultset I want
|vendorname |grandtotal|
---------------------
|Disney |30 |
|Gap |6 |
|ToysRus |6 |
|Najla |2 |
|Najla |10 |
If the vendorname = 'Najla' I want individual rows with their respective total otherwise I would like to group them and return a sum of their totals.
This is my query--
select *
from
(
select vendorname, sum(total) grandtotal
from vendor
where vendorname<>'Najla'
group by vendorname
union all
select vendorname, total grandtotal
from vendor
where vendorname='Najla'
) A
I was wondering if there's a better way to write this query instead of repeating it twice and performing a union. Is there a condensed way to group some rows "conditionally".
Honestly, I think the union all version is going to be the best performing and easiest to read option if it has appropriate indexes.
You could, however, do something like this (assuming you have a unique id on your table):
select vendorname, sum(total) grandtotal
from t
group by
vendorname
, case when vendorname = 'Najla' then id else null end
rextester demo: http://rextester.com/OGZQ33364
returns
+------------+------------+
| vendorname | grandtotal |
+------------+------------+
| Disney | 30 |
| Gap | 6 |
| ToysRus | 6 |
| Najla | 10 |
| Najla | 2 |
+------------+------------+

How to Merge 2 Rows into one by comma separate?

I need to merge this individual rows to one column, I now how to merge column by comma separated,
+---------------+-------+-------+
|CID |Flag |Value |
+---------------+-------+-------+
|1 |F |10 |
|1 |N |20 |
|2 |F |12 |
|2 |N |23 |
|2 |F |14 |
|3 |N |21 |
|3 |N |22 |
+---------------+-------+-------+
Desired Result can be anything,
+-----------+----------------------------+ +--------------------------+
|Part Number| Value | | Value |
+-----------+----------------------------+ +--------------------------+
| 1 | 1|F|10 ; 1|N|20 | Or | 1|F|10 ; 1|N|20 |
| 2 | 2|F|12 ; 2|N|23 ; 2|F|14 | | 2|F|12 ; 2|N|23 ; 2|F|14 |
| 3 | 3|N|21 ; 3|N|22 | | 3|N|21 ; 3|N|22 |
+-----------+----------------------------+ +--------------------------+
Note:
Any hint in right direction with small example is more than enough
EDIT :
I have massive data in tables like thousands of records where parent's and child relationship is present. I have to dump this into text files by comma separated values In single line as record. Think as primary record has relationship with so many other table then all this record has to be printed as a big line.
And I am trying to achieve by creating query so load can be distributed on database and only thing i have to worry about in business is just dumping logic into text files or whatever form we need in future.
You can try to use LISTAGG and your query will look like this:
select a.cid, a.cid || listagg(a.flag || '|' || a.value, ',')
from foo.dat a
group by a.cid
You can use different separators and of course play with how the result will be formatted.