How to make multiple row in one column with Case Statement? - sql

I need to join multiple row into one column. I have a table like this.
PRD_ID PRD_CONTAINER CONTAINER_LVL QTY
---------- --------------------- -------------
122 BIG_BOX 4
122 MED_BOX 3 6
122 SML_BOX 2
122 NO_BOX 1 50
All the qty value should be under one column starting with level 4 to 1. If the there is no value for Qty then it should be marked as 0/.
The result I want is like this
PRD_ID QTY
---------- -------
122 0/6/0/50
I tried this
SELECT
PRD_ID,
(CASE WHEN CONTAINER_LVL = 4 AND QTY > 0 THEN dbo.FBE_GetThousandSeparator(QTY,0) ELSE '0/' END +
CASE WHEN CONTAINER_LVL = 3 AND QTY > 0 THEN dbo.FBE_GetThousandSeparator(QTY,0) ELSE '0/' END +
CASE WHEN CONTAINER_LVL = 2 AND QTY > 0 THEN dbo.FBE_GetThousandSeparator(QTY,0) ELSE '0/' END +
CASE WHEN CONTAINER_LVL = 1 AND QTY > 0 THEN dbo.FBE_GetThousandSeparator(QTY,0) ELSE '' END) AS
QTY
FROM
#TMP2
GROUP BY
PRD_ID,
(CASE WHEN CONTAINER_LVL = 4 AND QTY > 0 THEN dbo.FBE_GetThousandSeparator(QTY,0) ELSE '0/' END +
CASE WHEN CONTAINER_LVL = 3 AND QTY > 0 THEN dbo.FBE_GetThousandSeparator(QTY,0) ELSE '0/' END +
CASE WHEN CONTAINER_LVL = 2 AND QTY > 0 THEN dbo.FBE_GetThousandSeparator(QTY,0) ELSE '0/' END +
CASE WHEN CONTAINER_LVL = 1 AND QTY > 0 THEN dbo.FBE_GetThousandSeparator(QTY,0) ELSE '' END)
My result is this:
PRD_ID QTY
---------- -------
122 0/0/0/
122 0/0/0/50
122 0/60/

Per #Larnu's comment, you can use string aggregation. Also, if you're just checking for nulls (rather than concerned with negatives) you don't need case expressions.
select PRD_ID,
string_agg(
convert(nvarchar(32), coalesce(QTY,0))
, '/'
) within group (order by CONTAINER_LVL desc) as QTY
from #TMP
group by PRD_ID
In the above we :
use coalesce to replace any null values with 0s
use convert to change from integers to strings
use string_agg to join all strings together, with / as a delimiter.
use within group (order by CONTAINER_LVL desc) to determine the order the fields are returned in (i.e. the QTY for CONTAINER_LVL=4 first, then 3, etc)
Related documentation: https://learn.microsoft.com/en-us/sql/t-sql/functions/string-agg-transact-sql?view=sql-server-ver16#f-generate-a-sorted-list-of-emails-per-towns
If that's not an option (i.e. as string_agg was only introduced in SQL 2017):
select PRD_ID,
convert(nvarchar(32), coalesce(max(case when CONTAINER_LVL = 4 then QTY end),0))
+ N'/' +
convert(nvarchar(32), coalesce(max(case when CONTAINER_LVL = 3 then QTY end),0))
+ N'/' +
convert(nvarchar(32), coalesce(max(case when CONTAINER_LVL = 2 then QTY end),0))
+ N'/' +
convert(nvarchar(32), coalesce(max(case when CONTAINER_LVL = 1 then QTY end),0))
from #TMP
group by PRD_ID
In the above we:
use the CASE expressions to get the QTY value for the given CONTAINER_LVL value for the given position; for all rows that don't have that CONTAINER_LVL we'd get NULL here. We also get NULL if the QTY value for that CONTAINER_LVL is NULL.
wrap that in MAX so that the multiple row's values (including NULLS) within the group are "squashed"/aggregated down to a single value; in this case the max. If all rows had NULLs, that would be NULL, if we had one numeric value and the rest null (as generally expected) we'd get that numeric value; if we had multiple numeric values (i.e. as we had 2 rows with identical PRD_ID and CONTAINER_LVL values) we'd get the one with the highest QTY value... though I'm assuming this last scenario isn't a valid one, and that you have a unique contraint to protect against it. Note: you could also use SUM here if you wish; then if you have multiple rows with the same PRD_ID and CONTAINER_LVL values their QTYs will be combined for the value at that position.
we wrap that in a coalesce function, such that if the value was NULL we replace that with a 0.
we wrap that in a convert function so we can perfom string concatenation on the value (i.e. '0' + '/' is valid... 0 + '/' is not).
we then use + 'N'/' + between each of these to concatenate the 4 values as expected.
SQL Fiddle Demo

Maybe using PIVOT can solve your problem, try this;
SELECT PROD_ID, [1] AS NO, [2] AS SML, [3] AS MED, [4] AS BIG
FROM #TMP2
PIVOT (
SUM(QTY)
FOR CONTAINER_LVL IN ([1], [2], [3], [4])
) P

Related

Trying to combine multiples of a key ID into single row, but with different values in columns

TSQL - SQL Sever
I'm building a report to very specific requirements. I'm trying to combine multiples of a key ID into single rows, but there's different values in some of the columns, so GROUP BY won't work.
SELECT count(tt.Person_ID) as CandCount, tt.Person_ID,
CASE e.EthnicSuperCategoryID WHEN CandCount > 1 THEN 10 ELSE e.EthnicSuperCategoryID END as EthnicSuperCategoryID,
CASE e.Ethnicity_Id WHEN 1 THEN 1 ELSE 0 END as Black ,
CASE e.Ethnicity_Id WHEN 2 THEN 1 ELSE 0 END as White ,
CASE e.Ethnicity_Id WHEN 3 THEN 1 ELSE 0 END as Asian,
etc
FROM T_1 TT
JOINS
WHERE
GROUP
Msg 102, Level 15, State 1, Line 4
Incorrect syntax near '>'.
Here's the results (without the first CASE). Note person 3 stated multiple ethnicities.
SELECT count(tt.Person_ID) as CandCount, tt.Person_ID,
CASE e.Ethnicity_Id WHEN 1 THEN 1 ELSE 0 END as Black ,
CASE e.Ethnicity_Id WHEN 2 THEN 1 ELSE 0 END as White ,
CASE e.Ethnicity_Id WHEN 3 THEN 1 ELSE 0 END as Asian,
etc
FROM T_1 TT
JOINS
WHERE
GROUP
That’s expected, but the goal would be to assign multiple ethnicities to Ethnicity_Id of 10 (multiple). I also want them grouped on a single line.
So the end result would look like this:
So my issue is two fold. If the candidate has more than 2 ethnicities, assign the records to Ethnicity_Id of 10. I also need duplicated person IDs grouped into a single row, while displaying all of the results of the columns.
This should bring your desired result:
SELECT Person_ID
, ISNULL(ID_Dummy,Ethnicity_ID) Ethnicity_ID
, MAX(Black) Black
, MAX(White) White
, MAX(Asian) Asian
FROM #T T
OUTER APPLY(SELECT MAX(10) FROM #T T2
WHERE T2.Person_ID = T.Person_ID
AND T2.Ethnicity_ID <> T.Ethnicity_ID
)EthnicityOverride(ID_Dummy)
GROUP BY Person_ID, ISNULL(ID_Dummy,Ethnicity_ID)
You want conditional aggregation. Your query is incomplete, but the idea is:
select
person_id,
sum(case ethnicity_id = 1 then 1 else 0 end) as black,
sum(case ethnicity_id = 2 then 1 else 0 end) as white,
sum(case ethnicity_id = 3 then 1 else 0 end) as asian
from ...
where ...
group by person_id
You might want max() instead of sum(). Also I did not get the logic for column the second column in the desired results - maybe that's just count(*).
This would be my approach
SELECT
person_id,
CASE WHEN flag = 1 THEN Ethnicity_Id ELSE 10 END AS Ethnicity_Id,
[1] as black,
[2] as white,
[3] as asian
FROM
(
SELECT
person_id,
Ethnicity_Id as columns,
1 as n,
MAX(Ethnicity_Id) over(PARTITION BY person_id) as Ethnicity_Id,
COUNT(Ethnicity_Id) over(PARTITION BY person_id) as flag
FROM
#example
) AS SourceTable
PIVOT
(
MAX(n) FOR columns IN ([1], [2], [3])
) AS PivotTable;
Pivot the Ethnicity_Id column into multiples columns, Using constant
1 to make it complain with your expected result.
Using Max(Ethnicity_Id) with Partition By to get the original
Ethnicity_Id
Using Count(Ethnicity_Id) to flag if a need to raplace Ethnicity_Id
with 10 bc there is more that 1 row for that person_id
If you need to add more Ethnicitys add the ids in ... IN ([1], [2], [3]) ... and in the select

Transpose rows from split_string into columns

I'm stuck trying to transpose a set of rows into a table. In my stored procedure, I take a delimited string as input, and need to transpose it.
SELECT *
FROM string_split('123,4,1,0,0,5|324,2,0,0,0,4','|')
CROSS APPLY string_split(value,',')
From which I receive:
value value
123,4,1,0,0,5 123
123,4,1,0,0,5 4
123,4,1,0,0,5 1
123,4,1,0,0,5 0
123,4,1,0,0,5 0
123,4,1,0,0,5 5
324,2,0,0,0,4 324
324,2,0,0,0,4 2
324,2,0,0,0,4 0
324,2,0,0,0,4 0
324,2,0,0,0,4 0
324,2,0,0,0,4 4
The values delimited by | are client details. And within each client, there are six attributes, delimited by ,. I would like an output table of:
ClientId ClientTypeId AttrA AttrB AttrC AttrD
------------------------------------------------
123 4 0 0 0 5
324 2 0 0 0 4
What's the best way to go about this? I've been looking at PIVOT but can't make it work because it seems like I need row numbers, at least.
This answer assumes that row number function will "follow the order" of the string. If it does not you will need to write your own split that includes a row number in the resulting table. (This is asked on the official documentation page but there is no official answer given).
SELECT
MAX(CASE WHEN col = 1 THEN item ELSE null END) as ClientId,
MAX(CASE WHEN col = 2 THEN item ELSE null END) as ClientTypeId,
MAX(CASE WHEN col = 3 THEN item ELSE null END) as AttrA,
MAX(CASE WHEN col = 4 THEN item ELSE null END) as AttrB,
MAX(CASE WHEN col = 5 THEN item ELSE null END) as AttrC,
MAX(CASE WHEN col = 6 THEN item ELSE null END) as AttrD
FROM (
SELECT A.value as org, B.value as item,
ROW_NUMBER() OVER (partition by A.value) as col
FROM string_split('123,4,1,0,0,5|324,2,0,0,0,4','|') as A
CROSS APPLY string_split(A.value,',') as B
) X
GROUP BY org
You might get a message about nulls in aggregate function ignored. (I always forget which platforms care and which don't.) If you do you can replace the null with 0.
Note, this is not as fast and using a CTE to find the 5 commas in the string with CHARINDEX and then using SUBSTRING to extract the values. But I'm to lazy to write up that solution which I would need to test to get all the off by 1 issues right. Still, I suggest you do it that way if you have a big data set.
I know you already got it pretty much answered, but here you can find a PIVOT solution
select [ClientID],[ClientTypeId],[AttrA],[AttrB],[AttrC],[AttrD]
FROM
(
select case when ColumnRow = 1 then 'ClientID'
when ColumnRow = 2 then 'ClientTypeId'
when ColumnRow = 3 then 'AttrA'
when ColumnRow = 4 then 'AttrB'
when ColumnRow = 5 then 'AttrC'
when ColumnRow = 6 then 'AttrD' else null end as
ColumnRow,t.value,ColumnID from (
select ColumnID,z.value as stringsplit,b.value, cast(Row_number()
over(partition by z.value order by z.value) as
varchar(50)) as ColumnRow from (SELECT cast(Row_number() over(order by
a.value) as
varchar(50)) as ColumnID,
a.value
FROM string_split('123,4,1,0,0,5|324,2,0,0,0,4','|') a
)z
CROSS APPLY string_split(value,',') b
)t
) AS SOURCETABLE
PIVOT
(
MAX(value)
FOR ColumnRow IN ([ClientID],[ClientTypeId],[AttrA],[AttrB],[AttrC],
[AttrD])
)
AS
PivotTable

How to take the total sum of all objects within an object?

I am trying to get the total sum of all parts in a container. The way I am doing now, sum(weight), will only grab the first weight of the first part in the container. I want to grab all part weights where the container number is the same. There are many different container numbers in the table. I want the statement to work with different container numbers, and only insert the value in the row of the first occurrence of the container number.
http://s33.postimg.org/3t63t83hr/sumweight.png
Each part has a weight in the above. I want to tally those weights for each container number and sum it up on the first row like shown.
,(case when mu.master_unit_no is null
then c.Gross_weight
when mu.master_unit_no is not null
then sum(c.Gross_weight)+mut.tare_weight
end)
as 'Weight in LBS'
Right now I have this query but it returns just the first part weight + the tare weight. I want to grab the sum of all the parts for the container.
/* I-Dashboards Shipping Report */
/* ROTW 11-21-2015 */
select
p.part_no AS 'Part_Number'
,p.name AS 'Description'
,c.serial_no as 'S#'
,c.quantity AS 'Qty'
,cp.customer_part_No as 'F_NUMBER'
--,cast(mut.length AS varchar) + 'X' + Cast(mut.width as varchar) + 'X' + Cast(mut.Height as varchar) as 'dim MU'
,(CASE when mut.length is null
then 0
else cast(mut.length as int) end) as 'M_LEN'
,(CASE when mut.width is null
then 0
else cast(mut.width as int) end) As 'M_WD'
,(CASE when mut.height is null
then 0
else cast(mut.Height as int) end) AS 'M_HT'
,cast(pct.cube_length AS INT) as 'S_LEN'
,cast(pct.cube_width AS INT) AS 'S_WD'
,cast(pct.cube_height AS INT) AS 'S_HT'
,mut.tare_Weight as 'M_Tare_lbs'
,c.Gross_weight as 'Net_Wt_lbs'
,mu.master_unit_no as 'M Number'
,g.Booking_No as 'Booking_HAWB_Num'
,concat(g.cargo_container_no, '-', g.dock_code) as 'Container_ID'
,g.outbound_scac_code AS 'Carrier'
,concat(cast(pct.cube_length as int), 'x', cast(pct.cube_width as int), 'x', cast(pct.cube_height as int)) as 'BOX_DIMS_INCHES'
,(case when row_number() over (partition by mu.master_unit_no order by mu.master_unit_no) = 1
then concat(cast(mut.length as int), 'x', cast(mut.width as int), 'x', cast(mut.Height as int))
when mu.master_unit_no is null
then ''
end)
as 'PALLET_DIMS_INCHES'
,(case when g.booking_container_type_key = 6 THEN
'DIRECT'
when g.booking_container_type_key = 5 THEN
'AIR'
else 'CEVA-Ocean'
end) as 'Shipment Type'
,CASE
--WHEN(ROW_NUMBER() OVER (PARTITION BY mu.master_unit_no ORDER BY mu.master_unit_no)) = 1
--then (select sum((pct.cube_length*0.0254)*(pct.cube_width*0.0254)* (pct.cube_height*0.0254))
--from part_v_container c where c.master_unit_key = mu.master_unit_key)
when mu.master_unit_no is null
then (pct.cube_length*0.0254)*(pct.cube_width*0.0254)* (pct.cube_height*0.0254)
end as 'CBM'
,select c.*, CASE
WHEN(ROW_NUMBER() OVER (PARTITION BY mu.master_unit_no ORDER BY mu.master_unit_no)) = 1
THEN **(**select SUM(c.Gross_weight)+mut.tare_weight
from part_v_container c where c.master_unit_no = mu.master_unit_no**)** END AS 'Total Weight'
from part_v_container c
I'm trying to take the total sum of all the parts gross weight in a m number + the tare weight for that m number and store is as total weight.
Like Siyual said, add tables to help better our understanding. Until then I believe I have most of what you want.
Your table probably looks something like...
part_id container_ id Weight
------- ------------- ------
1 a 5
2 a 5
3 b 99
4 a 3
5 c 99
And you probably want a result like (example using container_id = a)...
Weight
------
13
Try this...
SELECT SUM(Weight) FROM someTable WHERE container_id = someContainer
In the case of the result example I gave I would do...
SELECT SUM(Weight) FROM someTable WHERE container_id = 'a'
I am not fully sure on what you mean by your last part "only insert the value in the row of the first occurrence of the container number". Why would you want this specifically?
EDIT 1
The final result should not have multiple container_id though. I did the following...
My table...
SELECT t1.container_id, SUM(t1.weight) FROM table_1 t1 JOIN table_1 t2 ON t1.part_id = t2.part_id GROUP BY t1.container_id
Result was...
EDIT 2
It took me a while but I think I got it :)
Table:
Query:
SELECT t.*, CASE
WHEN(ROW_NUMBER() OVER (PARTITION BY t.Container ORDER BY t.Container)) = 1
THEN (SELECT SUM(t2.Weight) FROM table1 t2 WHERE t2.Container = t.Container)
ELSE 0 END AS 'Total Weight'
FROM table1 t GROUP BY t.Container, t.Part, t.Weight
Results:
EDIT 3
This was your original...
select c.*, CASE
WHEN(ROW_NUMBER() OVER (PARTITION BY mu.master_unit_no ORDER BY mu.master_unit_no)) = 1
THEN select SUM(c.Gross_weight)+mut.tare_weight
from part_v_container c where c.master_unit_no = mu.master_unit_no END AS 'Total Weight'
This is what I would change (surrounded by two asterix on both sides EX: ** A **)...
select c.*, CASE
WHEN(ROW_NUMBER() OVER (PARTITION BY mu.master_unit_no ORDER BY mu.master_unit_no)) = 1
THEN **(**select SUM(c.Gross_weight)+mut.tare_weight
from part_v_container c where c.master_unit_no = mu.master_unit_no**)** END AS 'Total Weight'
You need the parenthesis because the code doesn't know where the end belongs to otherwise. The parenthesis allows SQL to know that the end belongs to the case statement. I also am not sure where the mu. and mut. come from. It seems like they belong to a different table that you never reference here?
I am not sure if you added it but after 'Total Weight' you are missing
from someTable group by (all things that are in your select aka things that will be output need to be here...see my previous example for a better understanding)
If you want, on your original question you can post screen shots of exactly what your tables look like (or manually create it) so I can use the names you use accurately and make it more easily understandable by you :)
EDIT 4
/* I-Dashboards Shipping Report */
/* ROTW 11-21-2015 */
select
p.part_no AS 'Part_Number'
,p.name AS 'Description'
,c.serial_no as 'S#'
,c.quantity AS 'Qty'
,cp.customer_part_No as 'F_NUMBER'
--,cast(mut.length AS varchar) + 'X' + Cast(mut.width as varchar) + 'X' + Cast(mut.Height as varchar) as 'dim MU'
,(CASE when mut.length is null
then 0
else cast(mut.length as int) end) as 'M_LEN'
,(CASE when mut.width is null
then 0
else cast(mut.width as int) end) As 'M_WD'
,(CASE when mut.height is null
then 0
else cast(mut.Height as int) end) AS 'M_HT'
,cast(pct.cube_length AS INT) as 'S_LEN'
,cast(pct.cube_width AS INT) AS 'S_WD'
,cast(pct.cube_height AS INT) AS 'S_HT'
,mut.tare_Weight as 'M_Tare_lbs'
,c.Gross_weight as 'Net_Wt_lbs'
,mu.master_unit_no as 'M Number'
,g.Booking_No as 'Booking_HAWB_Num'
,concat(g.cargo_container_no, '-', g.dock_code) as 'Container_ID'
,g.outbound_scac_code AS 'Carrier'
,concat(cast(pct.cube_length as int), 'x', cast(pct.cube_width as int), 'x', cast(pct.cube_height as int)) as 'BOX_DIMS_INCHES'
,(case when row_number() over (partition by mu.master_unit_no order by mu.master_unit_no) = 1
then concat(cast(mut.length as int), 'x', cast(mut.width as int), 'x', cast(mut.Height as int))
when mu.master_unit_no is null
then ''
end)
as 'PALLET_DIMS_INCHES'
,(case when g.booking_container_type_key = 6 THEN
'DIRECT'
when g.booking_container_type_key = 5 THEN
'AIR'
else 'CEVA-Ocean'
end) as 'Shipment Type'
,(case when row_number() over (partition by mu.master_unit_no order by mu.master_unit_no) = 1
then (pct.cube_length*0.0254)*(pct.cube_width*0.0254)*(pct.cube_height*0.0254)
when mu.master_unit_no is null
then (pct.cube_length*0.0254)*(pct.cube_width*0.0254)* (pct.cube_height*0.0254)
end)
as 'CBM'
,CASE
WHEN(ROW_NUMBER() OVER (PARTITION BY mu.master_unit_no ORDER BY mu.master_unit_no)) = 1
THEN (SELECT SUM(c.Gross_weight) + mut.tare_weight
from part_v_container c where c.master_unit_no = mu.master_unit_no) END AS 'Total Weight'
from part_v_container c
So this should have fixed my part. I do have an extra comment though. You have all these different prefixs (p., c., mut., mu., g., pct.). Where do you reference all of these? I can see where you reference c (it is right after the final from). Even in my part you use mut. but I don't know how you reference it. For example, c is useable because of from part_v_container c. c represents part_v_container. You can look into joins to help you get the other tables in there. If you want you can edit your original question and add all your tables to it (whether they are actual or examples). I just need to know the different table names and column names. I don't care about the actual data. I wish I personally knew you because this would be much easier in real time xD
EDIT 5
Using this table...
I used this query...
;WITH mult AS (SELECT (m.length*0.0254)*(m.width*0.0254)*(m.height*0.0254) AS multiply, m.container FROM measurement m)
, sumMult AS (SELECT SUM((m.length*0.0254)*(m.width*0.0254)*(m.height*0.0254)) AS sumMultiply, m.container FROM measurement m GROUP BY m.container)
, combine AS (SELECT s.sumMultiply AS sumMultiply, m.multiply AS multiply, m.container FROM mult m JOIN sumMult s ON m.container = s.container)
SELECT c.container, CASE WHEN (ROW_NUMBER() OVER (PARTITION BY c.container ORDER BY c.container)) = 1
THEN (SELECT c.sumMultiply)
ELSE (SELECT c.multiply)
END AS 'Cubic Meters'
FROM combine c GROUP BY c.container, c.sumMultiply, c.multiply
It SUMS all of the volumes for all parts in a container and displays it only in the first row (first part). The rest of the parts have their volume.
I can't completely convert it for you. I trust, since you have done it successfully in my previous queries, that you can convert it properly. I tried to keep the names for the table and columns as bland and recognizable as much as I could. It appears to be working how you want it. Incase you don't know what the ;WITH mult.... is...you can think of it like a function. Put the entire with statement (that is, mult, sumMult, combine) before your gigantic query. You can see in my query that my ;WITH is comes first (above) my SELECT query that produces the actual results.

Counting if data exists in a row

Hey guys I have the below sample data which i want to query for.
MemberID AGEQ1 AGEQ2 AGEQ2
-----------------------------------------------------------------
1217 2 null null
58458 3 2 null
58459 null null null
58457 null 5 null
299576 6 5 7
What i need to do is to lookup the table and if any AGEx COLUMN contains any data then it counts the number of times there is data for that row in each column
Results example:
for memberID 1217 the count would be 1
for memberID 58458 the count would be 2
for memberID 58459 the count would be 0 or null
for memberID 58457 the count would be 1
for memberID 299576 the count would be 3
This is how it should look like in SQL if i query the entire table
1 Children - 2
2 Children - 1
3 Children - 1
0 Children - 1
So far i have been doing it using the following query which isnt very efficient and does give incorrect tallies as there are multiple combinations that people can answer the AGE question. Also i have to write multiple queries and change the is null to is not null depending on how many children i am looking to count a person has
select COUNT (*) as '1 Children' from Member
where AGEQ1 is not null
and AGEQ2 is null
and AGEQ3 is null
The above query only gives me an answer of 1 but i want to be able to count the other columns for data as well
Hope this is nice and clear and thank you in advance
If all of the columns are integers, you can take advantage of integer math - dividing the column by itself will yield 1, unless the value is NULL, in which case COALESCE can convert the resulting NULL to 0.
SELECT
MemberID,
COALESCE(AGEQ1 / AGEQ1, 0)
+ COALESCE(AGEQ2 / AGEQ2, 0)
+ COALESCE(AGEQ3 / AGEQ3, 0)
+ COALESCE(AGEQ4 / AGEQ4, 0)
+ COALESCE(AGEQ5 / AGEQ5, 0)
+ COALESCE(AGEQ6 / AGEQ6, 0)
FROM dbo.table_name;
To get the number of people with each count of children, then:
;WITH y(y) AS
(
SELECT TOP (7) rn = ROW_NUMBER() OVER
(ORDER BY [object_id]) - 1 FROM sys.objects
),
x AS
(
SELECT
MemberID,
x = COALESCE(AGEQ1 / AGEQ1, 0)
+ COALESCE(AGEQ2 / AGEQ2, 0)
+ COALESCE(AGEQ3 / AGEQ3, 0)
+ COALESCE(AGEQ4 / AGEQ4, 0)
+ COALESCE(AGEQ5 / AGEQ5, 0)
+ COALESCE(AGEQ6 / AGEQ6, 0)
FROM dbo.table_name
)
SELECT
NumberOfChildren = y.y,
NumberOfPeopleWithThatMany = COUNT(x.x)
FROM y LEFT OUTER JOIN x ON y.y = x.x
GROUP BY y.y ORDER BY y.y;
I'd look at using UNPIVOT. That will make your wide column into rows. Since you don't care about what value was in a column, just the presence/absence of value, this will generate a row per not-null column.
The trick then becomes mashing that into the desired output format. It could probably have been done cleaner but I'm a fan of "showing my work" so that others can conform it to their needs.
SQLFiddle
-- Using the above logic
WITH HadAges AS
(
-- Find everyone and determine number of rows
SELECT
UP.MemberID
, count(1) AS rc
FROM
dbo.Member AS M
UNPIVOT
(
ColumnValue for ColumnName in (AGEQ1, AGEQ2, AGEQ3)
) AS UP
GROUP BY
UP.MemberID
)
, NoAge AS
(
-- Account for those that didn't show up
SELECT M.MemberID
FROM
dbo.Member AS M
EXCEPT
SELECT
H.MemberID
FROM
HadAges AS H
)
, NUMBERS AS
(
-- Allowable range is 1-6
SELECT TOP 6
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS TheCount
FROM
sys.all_columns AS SC
)
, COMBINATION AS
(
-- Link those with rows to their count
SELECT
N.TheCount AS ChildCount
, H.MemberID
FROM
NUMBERS AS N
LEFT OUTER JOIN
HadAges AS H
ON H.rc = N.TheCount
UNION ALL
-- Deal with the unlinked
SELECT
0
, NA.MemberID
FROM
NoAge AS NA
)
SELECT
C.ChildCount
, COUNT(C.MemberID) AS Instances
FROM
COMBINATION AS C
GROUP BY
C.ChildCount;
Try this:
select id, a+b+c+d+e+f
from ( select id,
case when age1 is null then 0 else 1 end a,
case when age2 is null then 0 else 1 end b,
case when age3 is null then 0 else 1 end c,
case when age4 is null then 0 else 1 end d,
case when age5 is null then 0 else 1 end e,
case when age6 is null then 0 else 1 end f
from ages
) as t
See here in fiddle http://sqlfiddle.com/#!3/88020/1
To get the quantity of persons with childs
select childs, count(*) as ct
from (
select id, a+b+c+d+e+f childs
from
(
select
id,
case when age1 is null then 0 else 1 end a,
case when age2 is null then 0 else 1 end b,
case when age3 is null then 0 else 1 end c,
case when age4 is null then 0 else 1 end d,
case when age5 is null then 0 else 1 end e,
case when age6 is null then 0 else 1 end f
from ages ) as t
) ct
group by childs
order by 1
See it here at fiddle http://sqlfiddle.com/#!3/88020/24

SQL - Find the binary representation from the place of '1's

It is really hard to find a good title for this.
Here is the question: I have a SELECT query GROUP BY a field which returns me up to three values (1,2,3). These values are representing the positions of '1' in a binary number.
In other words:
Query Output | Reult
0,1,2 | 7 (111)
1,2 | 6 (110)
3 | 1 (001)
- | 0 (000)
Ok, I know it is easy. But there are two constraints. First, I want a query not a function/store procedure. Second, the result should be a string (like '010') not the number.
I found the solution for integer value, but not the string (varchar)
SELECT COALESCE(sum(power(2, field)), 0) AS test FROM (
SELECT field FROM myTable GROUP BY field) a
I am using SQL server 2008, just in case.
I also have this solution, but this one cannot be extended to bigger number of outputs:
SELECT output =
CASE TEST
WHEN 0 THEN '000'
WHEN 1 THEN '001'
WHEN 2 THEN '010'
WHEN 3 THEN '011'
WHEN 4 THEN '100'
WHEN 5 THEN '101'
WHEN 6 THEN '110'
WHEN 7 THEN '111'
END
FROM(
select COALESCE(sum(power(2, 3 - field)), 0) as test from (
select field from myTable group by field) a) b
You can use binary and and string concatenation:
select (case when test&4 > 0 then '1' else '0' end) +
(case when test&2 > 0 then '1' else '0' end) +
(case when test&1 > 0 then '1' else '0' end)
from (select 6 as test) t;
If you are allergic to case statements, you could do this:
select CHAR(ascii(0) + (test&4)/4) +
CHAR(ascii(0) + (test&2)/2) +
CHAR(ascii(0) + (test&1)/1)
from (select 6 as test) t