Bring similar multiple rows of a column into single row using SQL - sql

I want to
Table name: Master_Vendors
Vendor_ID Subvendor_id Subvendor_type Name City State
1 abc 1 Johnny York MN
1 xyz 2 Meera Birmingham NY
1 gef 3 Gaurav Cochin NY
2 aaa 1 Laila Lima MA
2 bbb 2 Zebo Reno SC
2 ccc 3 Gina Pitts NY
I want one row per Vendor_ID and i cannot use any aggregations
Vendor_ID Subvendor_id_1 Name_1 City_1 State_1 Subvendor_id_2 Name_2 City_2 State_2
1 abc Johnny York NY xyz Meera Birmingham NY
2 aaa Laila Lima MA bbb Zebo Reno SC
since i cannot do aggregations i cannot use PIVOT; i have never used CTE's before can we acheive this using CTE?

I didn't put in all the fields just enough so you can see the technique. Note this only works when you know in advance how many subvendors you want to show in the query:
select a.vendor_id , a.subvendor_id as Subvendor_id 1, a. Name as name_1, b.subvendor_id as Subvendor_id 2, b. Name as name_1, c.subvendor_id as Subvendor_id 3, a. Name as name_3
From Master_Vendors a
left join Master_Vendors b on a.vendor_id = b.vendor_id and b.subvendor_type = 2
left join Master_Vendors c on a.vendor_id = c.vendor_id and c.subvendor_type = 3
where a.subvendor_type = 1
If every record would have all three subvendors then you can use inner joins.

You can do that by using the PIVOT function but only if you use MS SQL Server 2008 or Higher, Or simply use the CASE WHEN Statement as below:
SELECT Vendor_ID as 'Vendor_ID ',
case when Location_ID =1 then Location_ID END as 'Location_ID_1 '
,case when City ='York' OR City ='Lima' then City END as 'City_1 '
,case when [State]='MN' OR [State]='NA' then [State] END as 'State_1'
,case when Location_ID =2 then Location_ID END as 'Location_ID_2 '
,case when City ='Birmingham' OR City ='Reno' then City END as 'City_2 '
,case when [State]='NY' OR [State]='SC' then [State] END as 'State_2 '
,case when Location_ID =3 then Location_ID END as 'Location_ID_3 '
,case when City ='Cochin' then City END as 'City_3 '
,case when [State]='NY' then [State] END as 'State_3 '
FROM Master_Vendors

Related

How to assign filters to row number () function in sql

I am trying to extract only single row after name = system in each case where the town is not Austin.
In case 1001 there are 8 rows, row # 4 is system, output should be only the row with Name=Terry and Date Moved=7/4/2019 (Next entry with town /= Austin)
Case Name Town Date Moved Row #(Not in table)
1001 Ted Madisson 9/7/2018 1
1001 Joyal Boston 10/4/2018 2
1001 Beatrice Chicago 1/1/2019 3
1001 System Chicago 1/5/2019 4
1001 John Austin 4/11/2019 5
1001 Simon Austin 6/11/2019 6
1001 Terry Cleveland 7/4/2019 7
1001 Hawkins Newyork 8/4/2019 8
1002 Devon Boston 12/4/2018 1
1002 Joy Austin 12/7/2018 2
1002 Rachael Newyork 12/19/2018 3
1002 Bill Chicago 1/4/2019 4
1002 System Dallas 2/12/2019 5
1002 Phil Austin 3/16/2019 6
1002 Dan Seattle 5/18/2019 7
1002 Claire Birmingham 7/7/2019 8
Tried sub query with row number function and not in ('Austin') filter
ROW_NUMBER() OVER(PARTITION BY Case ORDER BY Moved_date ASC) AS ROWNUM
Please note there are > 10k cases.
You can try this below script-
WITH CTE AS
(
SELECT [Case],[Name],Town,[Date Moved],
ROW_NUMBER() OVER (PARTITION BY [Case] ORDER BY [Date Moved]) [Row #]
FROM your_table
)
SELECT A.*
FROM CTE A
INNER JOIN
(
SELECT C.[Case],C.Town,MAX(C.[Row #]) MRN
FROM CTE C
INNER JOIN
(
SELECT *
FROM CTE A
WHERE A.Name = 'System'
)D ON C.[Case] = D.[Case] AND C.[Row #] > D.[Row #]
AND C.Town = 'Austin'
GROUP BY C.[Case],C.Town
)B ON A.[Case] = B.[Case] AND A.[Row #] = B.MRN+1
Output is -
Case Name Town Date Moved Row #
1001 Terry Cleveland 7/4/2019 6
1002 Dan Seattle 5/18/2019 7
Here are three possibilities. I'm still concerned about ties though. The first one will return multiple rows while the others only one per case:
with matches as (
select t1."case", min(t2."Date Moved") as "Date Moved"
from Movements r1 inner join Movements t2 on t1."case" = t2."case"
where t1.name = 'System' and t2.Town <> 'Austin'
and t2."Date Moved" > t1."Date Moved"
group by t1."case"
)
select t.*
from Movements t inner join matches m
on m."case" = t."case" and m."Date Moved" = t."Date Moved";
select m2.*
from Movements m1 cross apply (
select top 1 * from Movements m2
where m2.Town <> 'Austin' and m2."Date Moved" > m1."Date Moved"
order by m2."Date Moved"
) as match
where m1.name = 'System';
with m1 as (
select *,
count(case when name = 'System') over (partition by "case" order by "Date Moved") as flag
from Movements
), m2 as (
select *,
row_number() over (partition by "case" order by "Date Moved") as rn
from m1
where flag = 1 and name <> 'System' and Town <> 'Austin'
)
select * from m2 where rn = 1;
I'm basically assuming this is SQL Server. You might need a few minor tweaks if not.
It also does not require a town named Austin to fall between the "System" row and the desired row as I do not believe that was a stated requirement.

SQL- How to concatenate two columns with varchar types?

The below SQL code is supposed to return a concatenated string of first and last name, but it always returns 0's. Does anyone know why (PS I'm a complete NEWB to programming)?
Everything else in my code I've tested and it's working fine, but for some reason the two varchars don't want to return a string. Also, I tried concatenating the memid + zipcode from the same data set and it worked fine, so I believe it has something to do with the data type
SELECT f.name,
CONCAT( m.firstname + " " + m.surname ) AS mem_name
FROM `Bookings` b
JOIN `Members` m ON m.memid = b.memid
JOIN `Facilities` f ON b.facid = f.facid
WHERE f.name LIKE '%Tennis%'
LIMIT 0 , 30
It returns this:
name mem_name
Table Tennis 0
Tennis Court 1 0
Tennis Court 1 0
Tennis Court 2 0
Table Tennis 0
Table Tennis 0
Tennis Court 1 0
Tennis Court 1 0
Tennis Court 1 0
Here is the two data types:
surname varchar(17) utf8_general_ci
firstname varchar(9) utf8_general_ci
This is what the 'members' table looks like
memid surname firstname address zipcode
1 Smith Darren 8 Bloomsbury Close, Boston 4321
2 Smith Tracy 8 Bloomsbury Close, New York 4321
3 Rownam Tim 23 Highway Way, Boston 23423
4 Joplette Janice 20 Crossing Road, New York 234
You don't have to use the + operator. Try this:
SELECT
f.name,
CONCAT( m.firstname, " ", m.surname ) AS mem_name
FROM `Bookings` b
JOIN `Members` m ON m.memid = b.memid
JOIN `Facilities` f ON b.facid = f.facid
WHERE f.name LIKE '%Tennis%'
LIMIT 0, 30
At times, the concat function does not take multiple arguments. try nesting your columns that are to be concatenated. Also, use a , instead of plus.
SELECT
f.name,
CONCAT(CONCAT( m.firstname, ' '), m.surname ) AS mem_name
FROM `Bookings` b
JOIN `Members` m ON m.memid = b.memid
JOIN `Facilities` f ON b.facid = f.facid
WHERE f.name LIKE '%Tennis%'
LIMIT 0, 30
Check whether this works.

Converting query with aggregate functions to a group by

What I am trying to do is extract what and how many orders are ordered by customers.
I am able to get all the data but what I want is to group it based on a TrackingID unique to each customer, and thus get only one row per customer, regardless of how many items ordered.
The Code I currently have is
Select OT.TrackingID As FW_ID
,( Select
SUBSTRING(CT.Name, 1, CHARINDEX(' ', CT.Name) - 1)
Where LEN(CT.Name) - LEN(REPLACE(CT.Name, ' ', '')) > 0
) As Forename
,( Select
SUBSTRING(CT.Name, CHARINDEX(' ', CT.Name) + 1, 8000)
Where LEN(CT.Name) - LEN(REPLACE(CT.Name, ' ', '')) > 0
) As Surname
,( Select CAST(1 as VARCHAR) + ' p1 male'
Where OT.ArticleNr = 1
And CT.GroupNr IN (2,5)) As Amount_male_t1
,( Select CAST(1 as VARCHAR) + ' p1 female'
Where OT.ArticleNr = 2
And CT.GroupNr IN (2,5)) As Amount_female_t1
,( Select CAST(1 as VARCHAR) + ' p2 male'
Where OT.ArticleNr = 1
And CT.GroupNr IN (3,6)) As Amount_male_t2
,( Select CAST(1 as VARCHAR) + ' p2 female'
Where OT.ArticleNr = 2
And CT.GroupNr IN (3,6)) As Amount_female_t2
From OrderTable As OT
JOIN CustomerTable As CT
ON OT.CustomerNr = CT.CustomerNr
JOIN CampaignTable As CT
ON OT.TrackingID = CT.TrackingID
Where CT.GroupNr IN (2,3,5,6)
And OT.NewOrder = 1
An example of what I can get from this is
FW_ID Forename Surname Amount_male_t1 Amount_female_t1 Amount_male_t2 Amount_female_t2
101 John Doe 1 p1 male NULL NULL NULL
101 John Doe NULL 1 p1 female NULL NULL
102 Steve Boss NULL NULL 1 p2 male NULL
102 Steve Boss NULL NULL 1 p2 male NULL
And what I want is
FW_ID Forename Surname Amount_male_t1 Amount_female_t1 Amount_male_t2 Amount_female_t2
101 John Doe 1 p1 male 1 p1 female NULL NULL
102 Steve Boss NULL NULL 2 p2 male NULL
Problem is that when I use Group By on OT.TrackingID I get an error when using MAX() on the names due to them being aggregated already and errors when trying to turn the package counters into COUNT() funktions.
Help would be most appreciated.
The joined tables looks something like this
OrderTable:
TrackingID CustomerNr OrderNr ArticleNr NewOrder OrderDate
101 10054 25 1 1 2014-06-09
101 10054 24 2 1 2014-06-09
102 10036 23 1 1 2014-06-08
102 10036 22 1 1 2014-06-07
103 10044 21 2 0 2014-06-06
CustomerTable
CustomerNr Name Adress ZipCode CustomerCreatedDate
10054 John Doe Upstreet 123456 2013-05-18
10036 Steve Boss Downstreet 234567 2014-06-07
10044 Eric Cartman Sidestreet 345678 2014-02-21
CampaignTable
TrackingID GroupNr ProductDescription
101 2 Group 2 & 5 are offered package 1
102 3 Group 3 & 6 are offered package 2
103 5 Group 2 & 5 are offered package 1
NOTE: If someone could give advice as to why my question is downvoted that would be most appreciated. I don't quite know what I've done wrong.
One approach would be to use a view, as mentioned. You can do this in-line in the query, and it doesn't need to be saved in the schema.
I've made a demo to show how this can be done with the sample table data you provided. From here if you wanted to change the representation of the data to a single line, you can just pivot it as shown here.
SELECT SplitNames.Forename, SplitNames.LastName FROM CustomerTable
INNER JOIN
(
SELECT CustomerNr,
SUBSTRING(CT.Name, 1, CHARINDEX(' ', CT.Name) - 1) As Forename,
SUBSTRING(CT.Name, CHARINDEX(' ', CT.Name), LEN(CT.Name) - 1) As LastName
FROM CustomerTable CT
) SplitNames ON CustomerTable.CustomerNr = SplitNames.CustomerNr
In general you should try to use as few subqueries in the select statement as possible, as it makes it impossible to properly aggregate any of your results.
You could use your query as a view and then use that view for a new query using distinct and count clauses.
I'm sorry, I understand this is not the best solution since it has two steps and maybe could be solve in a better way, but I can't find anything better, at moment, without any example data on a my db.

select a record from each group if it has given value in column otherwise any one record

Vendor
VendorID | City
1 LosAngels
2 HongKong
VendorDetail
VendorDetailID | DetailCity | VendorID
11 Cairo 1
12 MosCow 1
13 Budapest 1
14 NewDelhi 2
15 Cairo 2
Mastervalues
Text | Value
LosAngels LA
HongKong HK
Cairo CA
MosCow Mo
Budapest BU
NewDelhi ND
The query should return records for every group of VendorID the City should be #GivenCityValue if any of its record has that value in DetailCity Column otherwise the City Should be the value from City Column of Vendor table
This can be achieved with SubQuery and Case When expression.
SELECT VendorID,
(SELECT Text FROM Mastervalues
Where Value IN(CASE WHEN (SELECT COUNT(*)
FROM VendorDetail
WHERE VendorID = Vendor.VendorID AND DetailCity = #GivenCityValue)>0
THEN #GivenCityValue
ELSE Vendor.City END)) AS City
FROM Vendor
if the given value for City is #GivenCityValue = 'Moscow' the desired result is
VendorID | City
1 MO
2 HK
But I am trying to do in Join itself. Do we need any user defined aggregate function ?
Is there any way to do it using join ?
Try this one -
SELECT VendorID
, City
FROM Vendor
OUTER APPLY (
SELECT City = [text]
FROM Mastervalues
WHERE EXISTS(
SELECT COUNT(*)
FROM VendorDetail
WHERE VendorID = Vendor.VendorID
AND DetailCity = #GivenCityValue
AND value = DetailCity
) OR Vendor.City = value
) t

SQL Teradata query

I have a table abc which have many records with columns col1,col2,col3,
dept | name | marks |
science abc 50
science cvv 21
science cvv 22
maths def 60
maths abc 21
maths def 62
maths ddd 90
I need to order by dept and name with ranking as ddd- 1, cvv - 2, abc -3, else 4 then need to find out maximum mark of an individual. Expected result is
dept | name | marks |
science cvv 22
science abc 50
maths ddd 90
maths abc 21
maths def 62
. How may I do it.?
SELECT
dept,
name,
MAX(marks) AS mark
FROM
yourTable
GROUP BY
dept,
name
ORDER BY
CASE WHEN name = 'ddd' THEN 1
name = 'cvv' THEN 2
name = 'abc' THEN 3
ELSE 4 END
Or, preferably, have another table that includes the sorting order.
SELECT
yourTable.dept,
yourTable.name,
MAX(yourTable.marks) AS mark
FROM
yourTable
INNER JOIN
anotherTable
ON yourTable.name = anotherTable.name
GROUP BY
yourTable.dept,
youtTable.name
ORDER BY
anotherTable.sortingOrder
This should work:
SELECT Dept, Name, MAX(marks) AS mark
FROM yourTable
GROUP BY Dept, Name
ORDER BY CASE WHEN Name = 'ddd' THEN 1
WHEN Name = 'cvv' THEN 2
WHEN Name = 'ABC' THEN 3
ELSE 4 END