I am trying to combine data from two columns. In this process I need duplicates which exist in the table.
The Tables are:
Here is the Query which I am using for the combination.
Finally the result which I get.
In the result you can see that one GRN_No among 1 & 2 is deleted. I don't want the duplicates to be deleted.
So with duplicates the result should be.
bags - 10.00, bags - 10.00, bubbles - 50.00
The issue was the group by was collapsing the multiple occurrences of bags. See code below: First code to generate your tables then the solution.
DROP TABLE GRNItems
CREATE TABLE GRNItems (
GRN_No int,
Item_No int,
Item varchar(25),
Meters decimal(10,2)
)
DROP TABLE GRN
CREATE TABLE GRN (
GRN_No int,
[SID] int
)
INSERT INTO GRNItems
VALUES
( 1,1,'bags', 10.00)
,( 2,1,'bags', 10.00)
,( 3,1,'bubble', 50.00)
,( 4,1,'lawn', 10.00)
INSERT INTO GRN
VALUES
( 1,4)
,( 2,4)
,( 3,4)
,( 4,2)
select STUFF(
(select ', ' + CONVERT(Varchar(10), GRNItems.Item)+ ' - ' + CONVERT(NVARCHAR(900), GRNItems.Meters)
--INTO #temp
From GRN INNER JOIN
GRNItems ON GRN.GRN_No = GRNItems.GRN_No
WHERE GRN.SID = 4
for xml path('')
),1,2, '') as [Items - Qty]
Result: bags - 10.00, bags - 10.00, bubble - 50.00
Related
I am trying to convert my rows into columns, or my columns into rows... I am a little confused with which it is exactly but here's what I would want it to look like
Original table:
Month | Price
1 500
2 600
3 700
what it needs to look like:
1 2 3
500 600 700
Could anyone tell me how his could be done?
EDIT:
CREATE table #yourtable (
[Id] int,
[Value] varchar(6),
[ColumnName] varchar(13)) ;
INSERT INTO #yourtable (
[Id],
[Value],
[ColumnName])
VALUES
(1, '1', 'Month'),
(2, '500', 'Price') ;
select
Month,
Price
from (
select
value,
columnname
from #yourtable ) d
pivot
(max(value) for columnname in (Month, Price) ) piv;
You wrote an almost correct query.
select
Month,
Price
from (
select
value,
columnname
from #yourtable) d
pivot
(max(value) for columnname in ('Month' AS Month, 'Price' AS Price) ) piv;
I have a dbo.products where all the products are stored. These products have a unique ID, Name, category and Price.
In a customer database (dbo.customers) the selected orders are pipe separated in one column. For example "12|61|42|48|56|57". Is it possible to get the total price based on what the customer selects? So if 12 is the ID of a price 10 product and 61 is a price 8 product the outcome should be 18 when the product column is "12|61"?
I've tried to refer to the id of the dbo.prices but I can only select one price at a time.
select Price
from [dbo].[products]
where id in (12|61|42|48|56|57)
So what I expect is the total value of all the selected product, please help.
Made third table as orders store customer selected products in that table ,
after that manage foreign key,primary key structure which will identify customer and products data.
then will get what you want.
use query
sum() for calculating total price etc.
So let's assume that you get your data from some external system, and you are forced to accept that orders will be pipe-delimited garbage. Let's also assume you don't have the capacity to split this raw data out into a normalised format, storing it in a proper, relational manner. So in this world you are stuck with a bad data model, and all you can do is query it.
First thing you will need is a way to split out the orders into a usable format. You could use the built in SPLIT function, but if you don't have access to this because you're on an older version of SQL Server then you could write your own. Here's an example:
CREATE FUNCTION [dbo].[split] (
#string VARCHAR(MAX),
#delimiter CHAR(1))
RETURNS TABLE
AS
RETURN
(
WITH Pieces(id, [start], [stop]) AS (
SELECT 1, 1, CONVERT(INT, CHARINDEX(#delimiter, #string))
UNION ALL
SELECT ID + 1, [stop] + 1, CONVERT(INT, CHARINDEX(#delimiter, #string, [stop] + 1)) FROM Pieces WHERE [stop] > 0
)
SELECT
id,
SUBSTRING(#string, [start], CASE WHEN [stop] > 0 THEN [stop] - [start] ELSE LEN(#String) END) AS item
FROM
Pieces
);
Once you have that in place you can do things like this:
SELECT * FROM dbo.split('12|61|42|48|56|57', '|');
Results:
id item
1 12
2 61
3 42
4 48
5 56
6 57
Now you can finally construct a query to bring all these parts together. Here's an example, using table variables, as I had to guess what your schema looks like:
DECLARE #customer TABLE (id INT, customer_name VARCHAR(20), [order] VARCHAR(50));
DECLARE #price TABLE (id INT, price INT);
INSERT INTO #customer SELECT 1, 'Fred', '1|2|3';
INSERT INTO #customer SELECT 2, 'Bill', '2|4';
INSERT INTO #customer SELECT 3, 'Mary', '3';
INSERT INTO #price SELECT 1, 5;
INSERT INTO #price SELECT 2, 3;
INSERT INTO #price SELECT 3, 2;
INSERT INTO #price SELECT 4, 1;
SELECT c.id, c.customer_name, COUNT(s.item) AS items_ordered, SUM(p.price) AS total_price FROM #customer c CROSS APPLY dbo.split(c.[order], '|') s INNER JOIN #price p ON p.id = s.item GROUP BY c.id, c.customer_name ORDER BY 1;
Results:
id customer_name items_ordered total_price
1 Fred 3 10
2 Bill 2 4
3 Mary 1 2
I am trying to figure out how to go about getting the values of a comma separated string that's present in one of my cells.
This is the query I current am trying to figure out in my stored procedure:
SELECT
uT.id,
uT.permissions
FROM
usersTbl AS uT
INNER JOIN
usersPermissions AS uP
/*Need to loop here I think?*/
WHERE
uT.active = 'true'
AND
uT.email = 'bbarker#thepriceisright.com'
The usersPermissions table looks like this:
And so a row in the usersTbl table looks like this for permissions:
1,3
I need to find a way to loop through that cell and get each number and place the name ****, in my returned results for the usersTbl.permissions.
So instead of returning this:
Name | id | permissions | age |
------------------------------------
Bbarker | 5987 | 1,3 | 87 |
It needs to returns this:
Name | id | permissions | age |
------------------------------------
Bbarker | 5987 | Read,Upload | 87 |
Really just replacing 1,3 with Read,Upload.
Any help would be great from a SQL GURU!
Reworked query
SELECT
*
FROM
usersTbl AS uT
INNER JOIN
usersPermissionsTbl AS uPT
ON
uPT.userId = uT.id
INNER JOIN
usersPermissions AS uP
ON
uPT.permissionId = uP.id
WHERE
uT.active='true'
AND
uT.email='bBarker#thepriceisright.com'
I agree with all of the comments... but strictly trying to do what you want, here's a way with a splitter function
declare #usersTbl table ([Name] varchar(64), id int, [permissions] varchar(64), age int)
insert into #usersTbl
values
('Bbarker',5987,'1,3',87)
declare #usersTblpermissions table (id int, [type] varchar(64))
insert into #usersTblpermissions
values
(1,'Read'),
(2,'Write'),
(3,'Upload'),
(4,'Admin')
;with cte as(
select
u.[Name]
,u.id as UID
,p.id
,p.type
,u.age
from #usersTbl u
cross apply dbo.DelimitedSplit8K([permissions],',') x
inner join #usersTblpermissions p on p.id = x.Item)
select distinct
[Name]
,UID
,age
,STUFF((
SELECT ',' + t2.type
FROM cte t2
WHERE t.UID = t2.UID
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
from cte t
Jeff Moden Splitter
CREATE FUNCTION [dbo].[DelimitedSplit8K] (#pString VARCHAR(8000), #pDelimiter CHAR(1))
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE! IT WILL KILL PERFORMANCE!
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
/* "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
enough to cover VARCHAR(8000)*/
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT TOP (ISNULL(DATALENGTH(#pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(#pString,t.N,1) = #pDelimiter
),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
SELECT s.N1,
ISNULL(NULLIF(CHARINDEX(#pDelimiter,#pString,s.N1),0)-s.N1,8000)
FROM cteStart s
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(#pString, l.N1, l.L1)
FROM cteLen l
;
GO
First, you should read Is storing a delimited list in a database column really that bad?, where you will see a lot of reasons why the answer to this question is Absolutely yes!
Second, you should add a table for user permissions since this is clearly a many to many relationship.
Your tables might look something like this (pseudo code):
usersTbl
(
Id int primary key
-- other user related columns
)
usersPermissionsTbl
(
UserId int, -- Foreign key to usersTbl
PermissionId int, -- Foreign key to permissionsTbl
Primary key (UserId, PermissionId)
)
permissionsTbl
(
Id int primary key,
Name varchar(20)
)
Once you have your tables correct, it's quite easy to get a list of comma separated values from the permissions table.
Adapting scsimon's sample data script to a correct many to many relationship:
declare #users table ([Name] varchar(64), id int, age int)
insert into #users values
('Bbarker',5987,87)
declare #permissions table (id int, [type] varchar(64))
insert into #permissions values
(1,'Read'),
(2,'Write'),
(3,'Upload'),
(4,'Admin')
declare #usersPermissions as table (userId int, permissionId int)
insert into #usersPermissions values (5987, 1), (5987, 3)
Now the query looks like this:
SELECT u.Name,
u.Id,
STUFF(
(
SELECT ','+ [type]
FROM #permissions p
INNER JOIN #usersPermissions up ON p.id = up.permissionId
WHERE up.userId = u.Id
FOR XML PATH('')
)
, 1, 1, '') As Permissions,
u.Age
FROM #Users As u
And the results:
Name Id Permissions Age
Bbarker 5987 Read,Upload 87
You can see a live demo on rextester.
I concur with much of the advice being presented to you in the other responses. The structure you're starting with is not going to be fun to maintain and work with. However, your situation may mean you are stuck with it so maybe some of the tools below will help you.
You can parse the delimiter with charindex() as others demonstrated here- MSSQL - How to split a string using a comma as a separator
... and even better here (several functions are provided) - Split function equivalent in T-SQL?
If you still want to do it with raw inline SQL and are committed to a loop, then pair the string manipulation with a CURSOR. Cursors have their own controversies BTW. The code below will work if your permission syntax remains consistent, which it probably doesn't.
They used charindex(',',columnName) and fed the location into the left() and right() functions along with some additional string evaluation to pull values out. You should be able to piece those together with a cursor
Your query might look like this...
--creating my temp structure
declare #userPermissions table (id int, [type] varchar(16))
insert into #userPermissions (id, [type]) values (1, 'Read')
insert into #userPermissions (id, [type]) values (2, 'Write')
insert into #userPermissions (id, [type]) values (3, 'Upload')
insert into #userPermissions (id, [type]) values (4, 'Admin')
declare #usersTbl table ([Name] varchar(16), id int, [permissions] varchar(8), age int)
insert into #usersTbl ([Name], id, [permissions], age) values ('Bbarker', 5987, '1,3', 87)
insert into #usersTbl ([Name], id, [permissions], age) values ('Mmouse', 5988, '2,4', 88)
--example query
select
ut.[Name]
, (select [type] from #userPermissions where [id] = left(ut.[permissions], charindex(',', ut.[permissions])-1) )
+ ','
+ (select [type] from #userPermissions where [id] = right(ut.[permissions], len(ut.[permissions])-charindex(',', ut.[permissions])) )
from #usersTbl ut
OK - I have a table - where I can have a row with multiple quanties - what I need to be able to do is to take all rows where there is a qty > 1 - create multiple rows - one for each qty - perform a simple calculation against each row - say multiply the val field by 2 - and then roll the rows back up into another temp table or something...?
DECLARE #table TABLE (id int IDENTITY(1,1),
code varchar(10),
codeStatus varchar,
qty int,
val money)
INSERT INTO #table
SELECT
'12345',
'T',
2,
1
A numbers table is your friend here.
The join against the numbers table effectively performs your expansion. The code below is essentially just the 'expansion' part of your question. After this its trivial to apply whatever transformations you need, push into temp table and so on.
/*
--create numbers table if don't already have one...
select top 1000000 row_number() over(order by t1.number) as N
into dbo.Numbers
from master..spt_values t1
cross join master..spt_values t2
*/
DECLARE #table TABLE (id int IDENTITY(1,1),code varchar(10), codeStatus varchar, qty int, val money)
INSERT INTO #table SELECT '12345', 'T', 2, 1
select t.id, t.code, t.codeStatus, t.qty, t.val
from #table t
inner join dbo.Numbers n on n.N <= t.qty
I have 2 tables:
'OrderHeader' with columns OrderNr and OrderContact.
'OrderDetail with columns OrderNr and OrderSerial.
For each ordernr there are max 3 orderserials.
So, what I'm trying to get is a table with columns:
OrderNr
OderContact
OrderSerial1
OrderSerial2
OrderSerial3
I'm now stuck with my pivot
SELECT OrderNr,
OderContact,
[1] as OrderSerial1,
[2] as OrderSerial2,
[3] as OrderSerial3
FROM
(select H.OrderNR as OrderNr,
OrderContact as OderContact,
OrderSerialsNr
from OrderHeader H inner join OrderDetail D on H.OrderNr = D.OrderNr
) AS PivSource
PIVOT
(count(OrderSerialsNr) for OrderSerialsNr in([1],[2],[3])) as pvt
I used the adventureworks DB for this.
USE [AdventureWorks2008R2]
GO
create view [test].[OrderHeader] (OrderNr,OrderContact)
as
SELECT SalesOrderID,
FirstName + ' ' + LastName
FROM Sales.SalesOrderHeader INNER JOIN
Sales.Customer ON Sales.SalesOrderHeader.CustomerID = Sales.Customer.CustomerID INNER JOIN
Person.Person ON Sales.Customer.PersonID = Person.Person.BusinessEntityID
GO
CREATE view [test].[OrderDetail] (OrderNr,OrderSerialsNr,Price)
as
SELECT [SalesOrderID]
,[ProductID]
,[unitPrice]
FROM [AdventureWorks2008R2].[Sales].[SalesOrderDetail]
where SalesOrderID in (select SalesOrderID
from [AdventureWorks2008R2].[Sales].[SalesOrderDetail]
group by SalesOrderID
having COUNT(SalesOrderID) < 4)
GO
Please help cause I don't know where to look anymore :).
To get data in pivot table the in claus should have correct data from table
you can get more idea from this query
if 1 and 2 are not in your table it will not appear as count and at a column value
CREATE TABLE #OrderHeader
(ORDERNR INT,
ORDERCONTACT CHAR(5))
CREATE TABLE #ORDERDETAIL
(ORDERNR INT,
ORDERSERIAL VARCHAR(10)
)
INSERT INTO #OrderHeader VALUES ( 1, 'X')
INSERT INTO #ORDERDETAIL VALUES ( 1, 'X1')
INSERT INTO #ORDERDETAIL VALUES ( 1, 'X2')
INSERT INTO #ORDERDETAIL VALUES ( 1, 1)
INSERT INTO #ORDERDETAIL VALUES ( 1, 2)
SELECT * FROM #OrderHeader
SELECT * FROM #ORDERDETAIL
SELECT OrderNr,
OderContact,
[1] as OrderSerial1,
[2] as OrderSerial2,
[3] as OrderSerial3
FROM
(select H.OrderNR as OrderNr,
OrderContact as OderContact,
ORDERSERIAL
from #OrderHeader H inner join #ORDERDETAIL D on H.OrderNr = D.OrderNr
) AS PivSource
PIVOT
(count(ORDERSERIAL) for ORDERSERIAL in([1],[2],[3])) as pvt
DROP TABLE #OrderHeader
DROP TABLE #ORDERDETAIL