Split values then resolve the values to a name - sql-server-2008

I need to be able to do something with my column (below) that can contain multiple values. The 'HearAboutEvent' column has multiple values separated by a comma. Each one of these values corresponds to an entry in another table. So the value of 11273 will equal facebook, 11274 will mean radio, and 11275 will mean commercial.
The data I am working with looks like this:
weather ID MemberID SubscriptionID DateEntered ParticipatedBefore ParticipatedBeforeCities WeatherDependent NonRefundable TShirtSize HearAboutEvent
Yes 24 18 1 2013-12-19 0 NULL 10950 10952 10957 11273, 11274, 11275
I am able to do the proper join to resolve the value of 'weather', note it is the first column and the 8th column.
This is the query I have created so far to resolve the values of WeatherDependent:
SELECT CFS1.Name as 'weather', *
FROM FSM_CustomForm_693 t
LEFT JOIN FSM_CustomFormSelectOptions CFS1 ON CFS1.ID = t.WeatherDependent
where t.ID = 24
Ultimately I need to have the data look like this:
weather ID MemberID SubscriptionID DateEntered ParticipatedBefore ParticipatedBeforeCities WeatherDependent NonRefundable TShirtSize HearAboutEvent
Yes 24 18 1 2013-12-19 0 NULL 10950 10952 10957 Facebook, radio, commercial

Things I think you could use to accomplish this are:
A Split TVF FUNCTION - http://msdn.microsoft.com/en-us/library/ms186755.aspx
CROSS APPLY - http://technet.microsoft.com/en-us/library/ms175156.aspx
STUFF & FOR XML PATH - http://msdn.microsoft.com/en-us/library/ms188043.aspx & http://msdn.microsoft.com/en-us/library/ms190922.aspx
Going one step further, you need something like this:
Excuse my profuse use of sub queries.
CREATE FUNCTION dbo.Split (#sep char(1), #s varchar(512))
RETURNS table
AS
RETURN (
WITH Pieces(pn, start, stop) AS (
SELECT 1, 1, CHARINDEX(#sep, #s)
UNION ALL
SELECT pn + 1, stop + 1, CHARINDEX(#sep, #s, stop + 1)
FROM Pieces
WHERE stop > 0
)
SELECT pn,
SUBSTRING(#s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS s
FROM Pieces
)
GO
SELECT
O.A,O.B,O.C,O.D,O.E,O.F,O.G,O.H,O.I,O.J,O.Stuffed
FROM (
SELECT
*
,STUFF((
SELECT ', ' + Name
FROM (
SELECT
V.*
,Y.Name
FROM (
SELECT
'Yes' AS A
,24 AS B
,18 AS C
,1 AS D
,'2013-12-19' AS E
,0 AS F
,NULL AS G
,10950 AS H
,10952 AS I
,10957 AS J
,'11273, 11274, 11275' AS K
)
AS V
CROSS APPLY dbo.Split(',',REPLACE(K,' ','')) AS P
JOIN (
SELECT 11273 AS Id , 'Facebook' AS Name UNION ALL
SELECT 11274 AS Id , 'radio' AS Name UNION ALL
SELECT 11275 AS Id , 'commercial' AS Name
)Y ON y.Id = p.s) ExampleTable
FOR XML PATH('')
), 1, 1, '' )
AS [Stuffed]
FROM (
SELECT
V.*
FROM (
SELECT
'Yes' AS A
,24 AS B
,18 AS C
,1 AS D
,'2013-12-19' AS E
,0 AS F
,NULL AS G
,10950 AS H
,10952 AS I
,10957 AS J
,'11273, 11274, 11275' AS K
)
AS V
CROSS APPLY dbo.Split(',',REPLACE(K,' ','')) AS P
JOIN (
SELECT 11273 AS Id , 'Facebook' AS Name UNION ALL
SELECT 11274 AS Id , 'radio' AS Name UNION ALL
SELECT 11275 AS Id , 'commercial' AS Name
)Y ON y.Id = p.s
)Z
) O
GROUP BY O.A,O.B,O.C,O.D,O.E,O.F,O.G,O.H,O.I,O.J,O.K,O.Stuffed

Related

Combining 2 Tables with an OUTER JOIN on Another Table

I need to combine 2 tables that may or may not have the data in them, but than I need a full outer join where the last table (if has content where IsActive = 1) gets shown that data, instead of the combined first 2 tables.
Currently have this:
( SELECT qp.ItemName AS name
, qp.TimeAdded AS created
, '' AS effective
, qp.VendorName AS supplier
, qp.Source AS source
, qp.VendorType AS type
, qp.Price AS cost
, '' AS price
, '' AS markup
, '' AS customer
, '' AS customerListID
, qp.VendorListID AS vendorListID
, '' AS itemListID
FROM wp_quantum_purchases AS qp
WHERE qp.IsActive = 1 AND
NOT EXISTS ( SELECT 1
FROM wp_hunter_quote_parts AS hqp
WHERE qp.ItemName = hqp.ItemName AND
hqp.IsActive = 1 ))
UNION ALL
( SELECT qs.ItemName AS name
, qs.TimeAdded AS created
, qs.SalesDate AS effective
, '' AS supplier
, qs.Source AS source
, '' AS type
, '' AS cost
, qs.Price AS price
, '' AS markup
, qs.CustomerName AS customer
, qs.CustomerListID AS customerListID
, '' AS vendorListID
, '' AS itemListID
FROM wp_quantum_sales AS qs
WHERE qs.IsActive = 1 AND
NOT EXISTS ( SELECT 1
FROM wp_hunter_quote_parts AS hqp
WHERE qs.ItemName = hqp.ItemName AND
hqp.IsActive = 1 ))
UNION ALL
( SELECT hqp.ItemName AS name
, hq.Quote_Date AS created
, hqp.SalesDate AS effective
, hqp.VendorName AS supplier
, hqp.Source AS source
, hqp.VendorType AS type
, hqp.Cost AS cost
, hqp.Price AS price
, CAST(( ( ( CAST(hqp.Price AS DECIMAL(10, 2)) - CAST(hqp.Cost AS DECIMAL(10, 2)) ) / CAST(hqp.Cost AS DECIMAL(10, 2)) ) * 100 ) AS DECIMAL(10, 2)) AS markup
, IFNULL(hq.Customer_FullName, 'N/A') AS customer
, hq.Customer_ListID AS customerListID
, hqp.VendorListID AS vendorListID
, hqp.Item_ListID AS itemListID
FROM wp_hunter_quote_parts AS hqp
LEFT JOIN wp_hunter_quotes AS hq
ON ( hq.id = hqp.QuoteID )
WHERE hqp.IsActive = 1)
ORDER BY NAME ASC;
But this is duplicating the data in 1st and 2nd tables and shows the data twice. I need the data from 1st and 2nd tables to be combined as 1 (if exists), but to prioritize the last table (wp_hunter_quote_parts) in here as the content to show from, if IsActive = 1 exists in the last table (wp_hunter_quote_parts). However, if IsActive = 1 does not exist in wp_hunter_quote_parts for ItemName than I would like to combine both wp_quantum_purchases and wp_quantum_sales as if it were 1 row.
Can not do a LEFT JOIN since data could exist in wp_quantum_purchases, but not in wp_quantum_sales OR data could exist in wp_quantum_sales and not in wp_quantum_purchases, OR data could not exist in either of these, and only exist in wp_hunter_quote_parts as well as data might not even exist in wp_hunter_quote_parts.
So, basically, if ItemName exists in wp_quantum_purchases AND IsActive = 1 AND wp_hunter_quote_parts does not have ItemName in table, get purchase data from wp_quantum_purchases, else if ItemName exists in wp_hunter_quote_parts get data from hunter_quote_parts instead.
If ItemName exists in wp_quantum_sales AND IsActive = 1 AND wp_hunter_quote_parts does not have ItemName in table, get sales data from wp_quantum_sales, else if ItemName exists in wp_hunter_quote_parts get data from hunter_quote_parts instead.
How can I combine first and second table, than do an outer join on it with another table?
Another Attempt here:
(SELECT IFNULL(qp.ItemName, qs.ItemName) AS name, IFNULL(qp.TimeAdded, qs.TimeAdded) AS created, qs.SalesDate AS effective, qp.VendorName AS supplier, qp.Source AS source, qp.VendorType AS type, qp.Price AS cost, qs.Price AS price, CAST((((CAST(qs.Price AS DECIMAL(10,2)) - CAST(qp.Price AS DECIMAL(10,2))) / CAST(qp.Price AS DECIMAL(10,2))) * 100) AS DECIMAL(10,2)) AS markup, qs.CustomerName AS customer, qs.CustomerListID AS customerListID, qp.VendorListID AS vendorListID, '' AS itemListID
FROM wp_quantum_purchases AS qp, wp_quantum_sales AS qs
WHERE (qp.IsActive = 1 OR qs.IsActive = 1)
AND NOT EXISTS (
SELECT 1
FROM wp_hunter_quote_parts AS hqp
WHERE (qp.ItemName = hqp.ItemName || qs.ItemName = hqp.ItemName) AND hqp.IsActive = 1
)
)
UNION ALL
(SELECT hqp.ItemName AS name, hq.Quote_Date AS created, hqp.SalesDate AS effective, hqp.VendorName AS supplier, hqp.Source AS source, hqp.VendorType AS type, hqp.Cost AS cost, hqp.Price AS price, CAST((((CAST(hqp.Price AS DECIMAL(10,2)) - CAST(hqp.Cost AS DECIMAL(10,2))) / CAST(hqp.Cost AS DECIMAL(10,2))) * 100) AS DECIMAL(10,2)) AS markup, IFNULL(hq.Customer_FullName, 'N/A') AS customer, hq.Customer_ListID AS customerListID, hqp.VendorListID AS vendorListID, hqp.Item_ListID AS itemListID
FROM wp_hunter_quote_parts AS hqp
LEFT JOIN wp_hunter_quotes AS hq ON (hq.id = hqp.QuoteID)
WHERE (hqp.IsActive = 1))
ORDER BY name ASC
Figured this one would work, but seems that it just keeps going and going and going, and doesn't seem to ever finish the query. No errors that I can see, but doesn't finish ever... And these tables are very small, that is odd...
I may not be understanding your question fully, but you could create a view of the first two tables and then do an outer join with the third table.

How to select all values of a field that has specific values in another field?

I have a table in which the field 'id_px' can have some repeated values because it has a different value in the field 'id_category' and I want to retrieve all values on 'id_px' that has the exact values that I pass. For example:
SELECT id_px FROM my_table WHERE id_category = 1 **AND** id_category = 32;
I found a way that retrieves me the result that I want:
SELECT id_px
FROM my_table
GROUP BY id_px
HAVING SUM(id_category NOT IN (1,32)) = 0
AND SUM(id_category = 1) = 1
AND SUM(id_category = 32) = 1
but I want to use it whitout the HAVING clause because I have to use some UNION clause.
Hope you can help me, thanks in advance.
I suggest some ways:
3- 3rd one: (Worked and marked as an Answer)
SELECT tbl.id_px FROM
(SELECT id_px ,
SUM(id_category NOT IN (1,32)) as b ,
SUM(id_category = 1) as t1 , SUM(id_category = 32) as t2
FROM my_table
GROUP BY id_px) as tbl
WHERE tbl.b = 0 AND tbl.t1 = 1 AND tbl.t2 = 1
4- Re-Building Query-2 :
4.1-first steps of rebuilding: (inefficient)
SELECT tbl.id_px FROM
(SELECT id_px , CONCAT('-,' , GROUP_CONCAT(id_category),',') as gr
FROM my_table
GROUP BY id_px
) as tbl
WHERE
INSTR( tbl.gr , ',1,' )>0
AND INSTR( tbl.gr , ',32,' )>0
AND LENGTH(tbl.gr)-LENGTH(REPLACE(REPLACE(tbl.gr,',1,' ,''), ',32,' ,'')) = LENGTH(CONCAT(',1,' , ',32,' ))
last condition in WHERE is replacing 1 and 32 from tbl.gr and comparing reduced LENGTH if reduced length is equal to 1 and 32 string, there were just 1 and 32 so rewrite query to this one:
4.2- New possibly working query: (simplified)
SELECT tbl.id_px FROM
(SELECT id_px , GROUP_CONCAT(id_category) as gr
FROM my_table
GROUP BY id_px
) as tbl
WHERE
tbl.gr = '1,32' or tbl.gr = '32,1'
Without any sample data it seems these are ok, but they didn't good answer while problem is limiting id_category to exactly have two values and no other id_category.
NOTE: These two queries, selects id_px that has id_category 1,32 and maybe many other id_category, so these are not acceptable answer for this question
2- Selecting id_px by join
SELECT a.id_px
FROM my_table as a
LEFT JOIN my_table as b
ON a.id_px = b.id_px
WHERE a.id_category = 1 AND b.id_category = 32
GROUP BY a.id_px
1- Using group by and searching in id_px:
SELECT tbl.id_px FROM
(SELECT id_px , CONCAT('-,' , GROUP_CONCAT(id_category),',') as gr
FROM my_table
GROUP BY id_px
) as tbl
WHERE
INSTR( tbl.gr , ',1,' )>0
AND INSTR( tbl.gr , ',32,' )>0

select from sql database with avg function

I have a database as shown below
id , name , var1
and I want to write a sql query like this:
select name
from table
where last var1 > avg of var1 s of each name
Notice that i want to select between names that last var1 is greater than avrage of var1s of each name
i write this code :
select name
from table
where var1>(select avg(var1) from table ) limit 0 , 1
but this code gets avrage from all var1s and I dont know whether this works or not!
for example we have these data:
1 , John , 32
2 , John , 21
3 , Mike , 22
4 , John , 11
5 , Mike , 5
6 , Mike , 45
=> for John , we have: 32+21+11 /3 =21.3 , but the last data is 11 , so John shouldnt be chosen
=> for Mike , avrage of var1 is 24 , and last row for Mike is 45 that is greater than the avrage , so Mike should be chosen.
Can anyone help me?
SELECT y.* FROM
your_table y
JOIN
(
SELECT name, AVG(var1) AS av, MAX(id) AS mx
FROM your_table
GROUP BY name
) tab
ON y.name = tab.name
AND y.id = tab.mx
AND y.var1 > tab.av
Here is the code at SQL Fiddle
[EDIT]:
Based on your latest requirement, what you want to accomplish is LIMIT N within group, which can be done with the following query:
SET #N := 2;
SELECT * FROM
(
SELECT (#rownumber:= #rownumber + 1) AS rn, yt.*
FROM your_table yt,(SELECT #rownumber:= 0) nums
ORDER BY name, id
) k
JOIN
(
SELECT t.name, MAX(rn) AS MaxRN FROM
(
SELECT (#rownumber:= #rownumber + 1) AS rn, yt.*
FROM your_table yt,(SELECT #rownumber:= 0) nums
ORDER BY name, id
) t
GROUP BY name
) l
ON k.rn <= l.MaxRN AND k.rn > l.MaxRN - #N
Here #N variable holds number of records we want to select within each group
Check the code at SQL Fiddle
Now in an outer query we can take the avg of the resultset returned by above.
Let me know if you could accomplish what you wanted with my inputs.

Coalesce whole records in MySQL

I'm currently coallescing fields individually in MySQL queries, but I would like to coalesce whole records.
Is this possible?
SELECT la.id,
COALESCE(( SELECT name FROM lookup_changed l0,
( SELECT MAX(id) id
FROM lookup_changed
WHERE lookup_id = 26
) l1
WHERE l0.id = l1.id
), la.name) name,
COALESCE(( SELECT msisdn FROM lookup_changed l0,
( SELECT MAX(id) id
FROM lookup_changed
WHERE lookup_id = 26
) l1
WHERE l0.id = l1.id
), la.msisdn) msisdn
FROM lookup_added la
WHERE la.id = 26
#Alma Do - the pseudo-SQL is:
SELECT la.id,
MULTICOALESCE(( SELECT <name, msisdn> FROM lookup_changed l0,
( SELECT MAX(id) id
FROM lookup_changed
WHERE lookup_id = 26
) l1
WHERE l0.id = l1.id
), <la.name, la.msisdn>) <name, msisdn>
FROM lookup_added la
WHERE la.id = 26
Since COALESCE() "return[s] the first non-NULL argument", it sounds like you want to retreive the "first non-NULL result from a set for queries":
-- syntax error
SELECT COALESCE(
SELECT a FROM ta,
SELECT b FROM tb
);
-- roughly equates to
( SELECT a AS val FROM ta WHERE a IS NOT NULL ORDER BY a LIMIT 1 )
UNION
( SELECT b AS val FROM tb WHERE b IS NOT NULL ORDER BY b LIMIT 1 )
ORDER BY val LIMIT 1 ;
Comments:
I added ORDER BY clauses, otherwise "first row" means nothing
the inner LIMIT 1 clauses are optional (but allow early trimming of sub-results)
the parenthesis around the sub queries are mandatory

SQL statement to insert repetitive data

current situation is to add below value of A01, B03, Z11 and X21 in repetitive way in field code for 400 hundreds row of data in table BabyCode.
Above is current table - without value in 'Code" column
Above is to be updated table - repetitive value is added in 'Code' column
You can do this:
INSERT INTO BabyCode
SELECT Codes.Code
FROM
(
SELECT id
FROM
(
SELECT t3.digit * 100 + t2.digit * 10 + t1.digit + 1 AS id
FROM TEMP AS t1
CROSS JOIN TEMP AS t2
CROSS JOIN TEMP AS t3
) t
WHERE id <= 400
) t,
(
SELECT 1 AS ID, 'A01' AS Code
UNION ALL
SELECT 2, 'B03'
UNION ALL
SELECT 3, 'Z11'
UNION ALL
SELECT 4, 'X21'
) codes;
But you will need to define a temp table, to use as an anchor table:
CREATE TABLE TEMP (Digit int);
INSERT INTO Temp VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
SQL Fiddle Demo
This will insert 400 hundred rows of the values A01, B03, Z11, and X21, into the code column in the table BabyCode.
You could put the four values into a virtual table identical to that used in #Mahmoud Gamal's answer, and, if the ID values in your table start at 1 and are sequential (have neither gaps nor duplicates), you could use the following method to join to the virtual table and update the target's Code column:
UPDATE YourTable t
INNER JOIN (
SELECT 1 AS ID, 'A01' AS Code
UNION ALL SELECT 2, 'B03'
UNION ALL SELECT 3, 'Z11'
UNION ALL SELECT 4, 'X21'
) x
ON (t.ID - 1) MOD 4 + 1 = x.ID
SET t.Code = x.Code
;
Otherwise you could use variables to assign 1, 2, 3, 4 sequentially to every row of your table, then you would be able join to the virtual table using those values:
UPDATE YourTable t
INNER JOIN (
SELECT ID, #rnk := CASE WHEN #rnk = 4 THEN 0 ELSE #rnk END + 1 AS rnk
FROM YourTable
CROSS JOIN (SELECT #rnk := 0) x
ORDER BY ID
) r ON t.ID = r.ID
INNER JOIN (
SELECT 1 AS ID, 'A01' AS Code
UNION ALL SELECT 2, 'B03'
UNION ALL SELECT 3, 'Z11'
UNION ALL SELECT 4, 'X21'
) x
ON r.rnk = x.ID
SET t.Code = x.Code
;
Both queries can be played with at SQL Fiddle:
Method 1
Method 2