Multilevel Location - Output as delimited series - sql-server

I have this table with the intention of having multi-levels of locations: Location within a location. Been trying to no avail. Appreciate if any of you can help.
| Location_ID | Location | Location_ID1 |
| ----------- | -------------- | ------------ |
| 10001 | Warehouse A | |
| 10002 | Warehouse B | |
| 10003 | Rack A | 10001 |
| 10004 | Rack B | 10001 |
| 10005 | Top Shelf | 10003 |
| 10006 | Bottom Shelf | 10003 |
I want to select all of the Locations and populate a dropdown list with this value-label pair
|Value | Label |
|-------------|------------------------------------|
|10001 |Warehouse A |
|10003 |Warehouse A > Rack A |
|10005 |Warehouse A > Rack A > Top Shelf |
|10006 |Warehouse A > Rack A > Bottom Shelf |
|10004 |Warehouse A > Rack B |
|10002 |Warehouse B |

You can use an rCTE to iterate through the data here, and also delimit your data with a little concatenation:
SELECT *
INTO dbo.YourTable
FROM (VALUES(10001,'Warehouse A',NULL ),
(10002,'Warehouse B',NULL ),
(10003,'Rack A',10001),
(10004,'Rack B',10001),
(10005,'Top Shelf',10003),
(10006,'Bottom Shelf',10003))V(Location_ID,Location,Location_ID1);
GO
WITH rCTE AS(
SELECT Location_ID,
Location,
Location_ID1,
CONVERT(varchar(500),Location) AS Label
FROM dbo.YourTable YT
WHERE Location_ID1 IS NULL
UNION ALL
SELECT YT.Location_ID,
YT.Location,
YT.Location_ID1,
CONVERT(varchar(500),r.Label + ' > ' + YT.Location) AS Label
FROM dbo.YourTable YT
JOIN rCTE r ON YT.Location_ID1 = r.Location_ID)
SELECT *
FROM rCTe;
GO
DROP TABLE dbo.YourTable

Related

MySQLServer: Check if conditions exist in group, then label entire group

My goal is to add another column to an existing table, to see if the value/conditions exists in a group and appropriately labeling the entire group if it is present or not.
If a Team has one project with a budget >= 20M or Actual_Spend >=2.5M I want to label the Team and all it's projects as Table 1 in the Category column. Irrespective if the other projects within the same Team fit this criteria.
I will provide a SQL fiddle link w/ my solution: http://sqlfiddle.com/#!18/3ddaf/12/0
I'm ending up with two extra columns of "Team" and "Category" and not sure how they're ending up there.
Below is the end result I'm looking for. I'm open to better solutions than the one I provided.
Thank you for your time
| Team | ProjectID | Budget | Actual_Spend | State | Category |
|------|-----------|----------|--------------|------------|----------|
| Cyan | 2 | NULL | NULL | Utah | Table 1 |
| Blue | 1 | NULL | 3000000 | California | Table 1 |
| Cyan | 1 | 20000000 | 1000000 | Utah | Table 1 |
| Blue | 2 | 22000000 | NULL | California | Table 1 |
| Red | 1 | 7000000 | 1000000 | Washington | Table 2 |
| Red | 2 | 19999000 | 2490000 | Oregon | Table 2 |
| Gray | 1 | 19000000 | 2500000 | Utah | Table 1 |
| Gray | 1 | 10000000 | 500000 | Utah | Table 1 |
Providing code to create the dataset:
Create Table Source_Data
(
Team varchar(50),
ProjectID INT,
BUDGET INT,
Actual_Spend INT,
State varchar(max),
)
INSERT INTO Source_Data
VALUES
('Blue',1,NULL,3000000,'California'),
('Green',1,20000000,1000000,'Utah'),
('Blue',2,22000000,NULL,'California'),
('Green',2,NULL,NULL,'Utah'),
('Red',1,7000000,1000000,'Washington'),
('Red',2,19999000,2490000,'Oregon'),
('Yellow',1,19000000,2500000,'Utah'),
('Yellow',1,10000000,500000,'Utah');
I think that you are looking for window functions:
select
s.*,
min(case when Budget>=20000000 or Actual_Spend>=2500000 then 'Table1' else 'Table2' end)
over(partition by team) Category
from Source_Data s
If any of the records having the same team satisfies condition Budget>=20000000 or Actual_Spend>=2500000, the new column yields Table1, else it produces Table2.
Demo on DB Fiddle:
Team | ProjectID | Budget | Actual_Spend | State | Category
:--- | --------: | -------: | -----------: | :--------- | :-------
Blue | 2 | 22000000 | null | California | Table1
Blue | 1 | null | 3000000 | California | Table1
Cyan | 1 | 20000000 | 1000000 | Utah | Table1
Cyan | 2 | null | null | Utah | Table1
Gray | 1 | 19000000 | 2500000 | Utah | Table1
Gray | 1 | 10000000 | 500000 | Utah | Table1
Red | 1 | 7000000 | 1000000 | Washington | Table2
Red | 2 | 19999000 | 2490000 | Oregon | Table2

Update table combining rows based on the same column value

I'm having a table called table such that:
| id | name | city |
|----|-------|---------|
| 0 | Rose | Madrid |
| 1 | Alex | Lima |
| 2 | Rose | Sidney |
| 3 | Mario | Glasgow |
And I need to UPDATE the table so that two rows sharing the same name combined into a new one and deleted.
| id | name | city |
|----|-------|----------------|
| 1 | Alex | Lima |
| 3 | Mario | Glasgow |
| 4 | Rose | Madrid, Sidney |
I don't care if it has to be done in several SQL statements.
So far all I've done is to list the rows that are affected by this.
SELECT *
FROM table
WHERE name IN (
SELECT name
FROM table
GROUP BY name
HAVING COUNT(*) > 1
);
Assuming that id is auto increment primary key, you need an INSERT and a DELETE statement:
insert into tablename(name, city)
select name, group_concat(city, ',')
from tablename
group by name
having count(*) > 1;
delete from tablename
where instr(name, ',') = 0
and exists (
select 1 from tablename t
where t.id <> tablename.id and t.name = tablename.name
and ',' || t.city || ',' like '%,' || tablename.city || ',%'
);
See the demo.
Results:
| id | name | city |
| --- | ----- | ------------- |
| 1 | Alex | Lima |
| 3 | Mario | Glasgow |
| 4 | Rose | Madrid,Sidney |

Lead/Lag syntax help - how do you order the columns?

Thanks for your help/advice. I'm unclear about the 1,0 within the LAG expression - what is that and why isn't mine working?
Do I have to do two Order by for both lead and lag?
Select
* Customer,
Prod,
day,
current sold,
date,
lag[current sold,1,0] OVER(PARTITION BY customer ORDER BY date DESC) as Previous Day,
lead[current sold,1,0] OVER(PARTITION BY customer ORDER BY date DESC) as Next Day
From
table1
Result:
| PROD | DAY | CURRENT SOLD | date customer |
+-------+-----+--------------+-----------------------
| SHIRT | M | 2 | 1-2018 A
| SHIRT | T | 9 | 2-2018 B
| SHIRT | W | 0 | 12-2018 C
| SHIRT | TH | 6 | 11-2018 D
| SHIRT | F | 7 | 3-2018 E
+-------+-----+--------------+--+----------------
+-------+-----+--------------+---------------+-----------+--+--------------
| PROD | DAY | CURRENT SOLD | PREVIOUS SOLD | NEXT SOLD | date |customer
+-------+-----+--------------+---------------+-----------+--+---------------
| SHIRT | M | 2 | | 9 | 1-2018 |A
| SHIRT | T | 9 | 2 | 0 | 2-2018 |B
| SHIRT | W | 0 | 9 | 6 | 12-2018|C
| SHIRT | TH | 6 | 0 | 7 | 11-2018|D
| SHIRT | F | 7 | 6 | | 3-2018 |E
+-------+-----+--------------+---------------+-----------+--+---------------
you can use LAG for previous sales and LEAD for next sales. I prepared sample with your example :
--DROP TABLE #Tbl;
--DROP TABLE #Days;
CREATE TABLE #Tbl
(
Prod VARCHAR(10)
,[DayName] VARCHAR(2)
,CurrentSold INT
);
CREATE TABLE #Days
(
DayNumber INT
,[DayName] VARCHAR(2)
);
INSERT INTO #Days
VALUES (1,'M'),(2,'T'),(3,'W'),(4,'TH'),(5,'F');
INSERT INTO #Tbl
VALUES ('SHIRT','M',2)
,('SHIRT','T',9)
,('SHIRT','W',0)
,('SHIRT','TH',6)
,('SHIRT','F',7);
SELECT T.Prod
,T.DayName
,T.CurrentSold
,LAG(CurrentSold, 1,0) OVER (ORDER BY DayNumber) AS PreviousSold
,LEAD(CurrentSold, 1,0) OVER (ORDER BY DayNumber) AS PreviousSold
FROM #Tbl T
INNER JOIN #Days D ON T.DayName = D.DayName;

SQL Server: Returning rows with multiple and distinct values

I've been working on this issue for the last day and a half and just can't seem to find another question on here that works for my code.
I have a table here:
Table_D
Policynumber| EntryDate | BI_Limit | P remium
------------------------------------------------------
ABCD100001 | 5/1/16 | 15/30 | 919
ABCD100001 | 5/13/16 | 15/30 | 1008
ABCD100002 | 5/24/16 | 100/300 | 1380
ABCD100003 | 5/30/16 | 25/50 | 1452
ABCD100003 | 6/2/16 | 25/50 | 1372
ABCD100003 | 6/4/16 | 30/60 | 951
ABCD100004 | 6/11/16 | 100/300 | 1038
ABCD100005 | 6/22/16 | 100/300 | 1333
ABCD100005 | 7/2/16 | 50/100 | 1208
ABCD100006 | 7/10/16 | 250/500 | 1345
ABCD100007 | 7/18/16 | 15/30 | 996
in which I'm trying to extract rows in which a policynumber has multiple listings and a different BI_Limit. So the output should be:
Output
Policynumber | EntryDate | BI_Limit | Premium
---------------------------------------------------
ABCD100003 | 5/30/16 | 25/50 | 1452
ABCD100003 | 6/2/16 | 25/50 | 1372
ABCD100003 | 6/4/16 | 30/60 | 951
ABCD100005 | 6/22/16 | 100/300 | 1333
ABCD100005 | 7/2/16 | 50/100 | 1208
I'm storing Policynumber as VARCHAR(Max), EntryDate as DATE, BI_Limit as VARCHAR(Max), and Premium as INTEGER.
The code I've want to say should work would be something along the lines of:
SELECT * FROM Table_D
WHERE BI_Limit IN (
SELECT BI_Limit
FROM Table_D
GROUP BY BI_Limit
HAVING COUNT(DISTINCT BI_Limit)>1);
But this returns nothing for me. Can anyone help to show me what I'm doing wrong? Thank you.
You could also try exists
select a.*
from Table_D a
where
exists (
select 1
from Table_D b
where a.Policynumber = b.Policynumber
and a.BI_Limit <> b.BI_Limit
)
SELECT d.*
FROM ( -- find the policy number with multiple listing and diff BI_Limit
SELECT PolicyNumber
FROM TableD
GROUP BY PolicyNumber
HAVING count(*) > 1
AND MIN (BI_Limit) <> MAX (BI_Limit)
) m -- join back the Table_D to for other information
INNER JOIN Table_D d
ON m.PolicyNumber = d.PolicyNumber

Group By same or similar string sql

1) Suppose i have a table like this:-
| id | color_code | fruit |
|:------|--------------|----------------:|
| 1 | 000001 | apple |
| 2 | 000001 | apple |
| 3 | 000001 | apple |
| 4 | 000002 | lemon |
| 5 | 000002 | lemon |
| 6 | 000003 | grapes |
| 7 | 000003 | grapes |
How can i group by the fruit column according to the color_code column in sql server?
like this i suppose:-
| id | color_code | fruit | group_concat(id) |
|:------|--------------|-----------------|---------------------|
| 1 | 000001 | apple | 1,2,3 |
| 4 | 000002 | lemon | 2,5 |
| 6 | 000003 | grapes | 6,7 |
2) What if i have 3 tables (like shown below) which require join, how can i achieve this?
series_no table:
| id | desc_seriesno |
|:------|----------------:|
| 7040 | AU1011 |
| 7041 | AU1022 |
| 7042 | AU1033 |
| 7043 | AU1044 |
| 7044 | AU1055 |
| 7045 | AU1066 |
brand table:
| id | desc_brand |
|:------|----------------:|
| 1020 | Audi |
| 1021 | Bentley |
| 1022 | Ford |
| 1023 | BMW |
| 1024 | Mazda |
| 1025 | Toyota |
car_info table:
| seriesno_id | brand_id | color |
|:---------------|------------|--------:|
| 7040 | 1020 | white |
| 7040 | 1020 | black |
| 7040 | 1020 | pink |
| 7041 | 1021 | yellow |
| 7041 | 1021 | brown |
| 7042 | 1022 | purple |
| 7042 | 1022 | black |
| 7042 | 1022 | green |
| 7043 | 1023 | blue |
| 7044 | 1024 | red |
| 7045 | 1025 | maroon |
| 7045 | 1025 | white |
this is my current query with sql server 2014:-
SELECT SN.id AS seriesid, B.id AS brandid, B.desc_brand
FROM [db1].[dbo].[series_no] SN
LEFT JOIN [db1].[dbo].[car_info] CI
ON CI.seriesno_id = SN.id
RIGHT JOIN [db1].[dbo].[brand] B
ON B.id = CI.brand_id
GROUP BY SN.id, B.id
ORDER BY SN.id ASC
but unfortunately it gave me an error since i cannot group by similar string this way.
i want it to be like this:-
| seriesid | brandid | desc_brand | count |
|:-----------|------------|---------------|-------|
| 7040 | 1020 | Audi | 3 |
| 7041 | 1021 | Bentley | 2 |
| 7042 | 1022 | Ford | 3 |
| 7043 | 1023 | BMW | 1 |
| 7044 | 1024 | Mazda | 1 |
| 7045 | 1025 | Toyota | 2 |
1 Fruit Color
Assuming the table name is FruitColor, you can get the desired output by the following query -
SELECT MIN(id) AS id
, color_code
, fruit
, group_concat_id = STUFF((SELECT ',' + CAST(id AS VARCHAR)
FROM FruitColor AS fci
WHERE fci.fruit = fc.fruit AND fci.color_code = fc.color_code
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM FruitColor AS fc
GROUP BY color_code, fruit
ORDER BY id;
The MIN() selects the first id of the group.
Since there is no default GROUP_CONCAT function like in MySql in SQL Server, you have to use the STUFF function and FOR XML PATH. To learn more about group concat you can visit this link https://sqlperformance.com/2014/08/t-sql-queries/sql-server-grouped-concatenation
You can customize the WHERE clause to match only by color_code.
2. You can have several options for this -
Option (a): Show counts for series with brands
SELECT seriesno_id AS seriesid, ci.brand_id AS bandid, desc_brand, COUNT(*) AS [count]
FROM db1.dbo.car_info AS ci
LEFT JOIN db1.dbo.brand AS b ON (b.id = ci.brand_id)
GROUP BY seriesno_id, ci.brand_id, desc_brand;
Here you don't need to use the series table if you want to show counts for cars having brand(s).
You may not need to use the RIGHT JOIN on the brand table because if brand table contains a record which
is not in car_info table, then seriesno_id would be null.
Option (b): Show counts for all the series with or without a brand
SELECT sn.id AS seriesid, ci.brand_id AS bandid, desc_brand, COUNT(*) AS [count]
FROM db1.dbo.series_no AS sn
LEFT JOIN db1.dbo.car_info AS ci ON (ci.seriesno_id = sn.id)
LEFT JOIN db1.dbo.brand AS b ON (b.id = ci.brand_id)
GROUP BY sn.id, ci.brand_id, desc_brand;
Option (c): The work around for selecting a column which is not in a GROUP BY
SELECT seriesno_id AS seriesid, ci.brand_id AS bandid, MAX(desc_brand) AS desc_brand, COUNT(*) AS [count]
FROM db1.dbo.car_info AS ci
LEFT JOIN db1.dbo.brand AS b ON (b.id = ci.brand_id)
GROUP BY seriesno_id, ci.brand_id;
Here, if we are certain that each brand contains only one desc_brand, we can use an aggregate on it.
This is bcause applying aggregate only one value returns that value. I used MAX here.
Personally I would go with option (a) as it makes more sense.
Update on GROUP BY exception for desc_brand being NTEXT...
Cast desc_brand to NVARCHAR to avoid the exception.
CAST(desc_brand AS NVARCHAR(200))
Also I highly recommend using VARCHAR / NVARCHAR instead of any TEXT, CHAR etc. because they usually occupy more memory.
SELECT
id = SUBSTRING(group_concat,1,1),
color_code,
fruit,
group_concat
FROM(
SELECT distinct
m.color_code,
m.fruit,
group_concat = STUFF((SELECT ',' + CONVERT(varchar(10),md.id)
FROM [Test_1].[dbo].[Stuff] md
WHERE m.fruit = md.fruit
AND m.color_code = md.color_code
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM [Test_1].[dbo].[Stuff] m)x
use below code ..
SELECT distinct
m.color_code
, m.fruit
, group_concat = STUFF((
SELECT ',' + CONVERT(varchar(10),md.id)
FROM dbo.tablename md
WHERE m.fruit = md.fruit and m.color_code = md.color_code
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM dbo.tablename m
for second :
SELECT SN.id AS seriesid, B.id AS brandid, B.desc_brand ,count(*)
FROM [db1].[dbo].[series_no] SN
LEFT JOIN [db1].[dbo].[car_info] CI
ON CI.seriesno_id = SN.id
RIGHT JOIN [db1].[dbo].[brand] B
ON B.id = CI.brand_id
GROUP BY SN.id, B.id ,B.desc_brand
ORDER BY 4 ASC

Resources